-QT LICENSE AGREEMENT
-Agreement version 4.4.1
-
-This Qt License Agreement ("Agreement") is a legal agreement for the licensing
-of Licensed Software (as defined below) between The Qt Company (as defined
-below) and the Licensee who has accepted the terms of this Agreement by signing
-this Agreement or by downloading or using the Licensed Software or in any other
-appropriate means.
-
-Capitalized terms used herein are defined in Section 1.
-
-WHEREAS:
- (A) Licensee wishes to use the Licensed Software for the purpose of
- developing and distributing Applications and/or Devices (each as defined
- below);
- (B) The Qt Company is willing to grant the Licensee a right to use Licensed
- Software for such a purpose pursuant to term and conditions of this
- Agreement; and
- (C) Parties wish to enable that their respective Affiliates also can sell
- and purchase licenses to serve Licensee Affiliates' needs to use Licensed
- Software pursuant to terms of the Agreement. Any such license purchases by
- Licensee Affiliates from The Qt Company or its Affiliates will create
- contractual relationship directly between the relevant The Qt Company and
- the respective ordering Licensee Affiliate "Acceding Agreement").
- Accordingly, Licensee shall not be a party to any such Acceding Agreement,
- and no rights or obligations are created to the Licensee thereunder but all
- rights and obligations under such Acceding Agreement are vested and borne
- solely by the ordering Licensee Affiliate and the relevant The Qt Company
- as a contracting parties under such Acceding Agreement.
-
-NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS:
-
-1. DEFINITIONS
-
-"Affiliate" of a Party shall mean an entity
- (i) which is directly or indirectly controlling such Party;
- (ii) which is under the same direct or indirect ownership or control as
- such Party; or
- (iii) which is directly or indirectly owned or controlled by such Party.
-For these purposes, an entity shall be treated as being controlled by another
-if that other entity has fifty percent (50 %) or more of the votes in such
-entity, is able to direct its affairs and/or to control the composition of its
-board of directors or equivalent body.
-
-"Add-on Products" shall mean The Qt Company's specific add-on software products
-which are not licensed as part of The Qt Company's standard product offering,
-but shall be included into the scope of Licensed Software only if so
-specifically agreed between the Parties.
-
-"Agreement Term" shall mean the validity period of this Agreement, as set forth
-in Section 12.
-
-"Applications" shall mean software products created using the Licensed
-Software, which include the Redistributables, or part thereof.
-
-"Contractor(s)" shall mean third party consultants, distributors and
-contractors performing services to the Licensee under applicable contractual
-arrangement.
-
-"Customer(s)" shall mean Licensee's customers to whom Licensee, directly or
-indirectly, distributes copies of the Redistributables as integrated or
-incorporated into Applications or Devices.
-
-"Data Protection Legislation" shall mean the General Data Protection Regulation
-(EU 2016/679) (GDPR) and any national implementing laws, regulations and
-secondary legislation, as may be amended or updated from time to time, as well
-as any other data protection laws or regulations applicable in relevant
-territory.
-
-"Deployment Platforms" shall mean target operating systems and/or hardware
-specified in the License Certificate, on which the Redistributables can be
-distributed pursuant to the terms and conditions of this Agreement.
-
-"Designated User(s)" shall mean the employee(s) of Licensee or Licensee's
-Affiliates acting within the scope of their employment or Licensee's
-Contractors acting within the scope of their services on behalf of Licensee.
-
-"Development License" shall mean the license needed by the Licensee for each
-Designated User to use the Licensed Software under the license grant described
-in Section 3.1 of this Agreement. Development Licenses are available per
-respective Licensed Software products, each product having its designated scope
-and purpose of use.
-
-"Development License Term" shall mean the agreed validity period of the
-Development License or QA Tools license during which time the relevant Licensed
-Software product can be used pursuant to this Agreement. Agreed Development
-License Term, as ordered and paid for by the Licensee, shall be memorialized in
-the applicable License Certificate.
-
-"Development Platforms" shall mean those host operating systems specified in
-the License Certificate, in which the Licensed Software can be used under the
-Development License.
-
-"Devices" shall mean
- (1) hardware devices or products that
- i. are manufactured and/or distributed by the Licensee, its Affiliates,
- Contractors or Customers, and
- ii. incorporate, integrate or link to Applications such that
- substantial functionality of such unit, when used by an End User,
- is provided by Application(s) or otherwise depends on the Licensed
- Software, regardless of whether the Application is developed by
- Licensee or its Contractors; or
- (2) Applications designed for the hardware devices specified in item (1).
-
- Devices covered by this Agreement shall be specified in Appendix 2 or in a
- quote.
-
-"Distribution License(s)" shall mean a royalty-bearing license required for any
-kind of sale, trade, exchange, loan, lease, rental or other distribution by or
-on behalf of Licensee to a third party of Redistributables in connection with
-Devices pursuant to license grant described in Section 3.3 of this Agreement.
-Distribution Licensed are sold separately for each type of Device respectively
-and cannot be used for any type of Devices at Licensee's discretion.
-
-"Distribution License Packs" shall mean set of prepaid Distribution Licenses
-for distribution of Redistributables, as defined in The Qt Company's standard
-price list, quote, Purchase Order confirmation or in an Appendix 2 hereto, as
-the case may be.
-
-"End User" shall mean the final end user of the Application or a Device.
-
-"Evaluation License Term" shall mean a time period specified in the License
-Certificate for the Licensee to use the relevant Licensed Software for
-evaluation purposes according to Section 3.6 herein.
-
-"Intellectual Property Rights" shall mean patents (including utility models),
-design patents, and designs (whether or not capable of registration), chip
-topography rights and other like protection, copyrights, trademarks, service
-marks, trade names, logos or other words or symbols and any other form of
-statutory protection of any kind and applications for any of the foregoing as
-well as any trade secrets.
-
-"License Certificate" shall mean a certificate generated by The Qt Company for
-each Designated User respectively upon them downloading the Licensed Software,
-which will be available under respective Designated User's Qt Account at
-account.qt.io. License Certificates will specify relevant information
-pertaining the Licensed Software purchased by Licensee and Designated User's
-license to the Licensed Software.
-
-"License Fee" shall mean the fee charged to the Licensee for rights granted
-under the terms of this Agreement.
-
-"Licensed Software" shall mean specified product of commercially licensed
-version of Qt Software and/or QA Tools defined in Appendix 1 and/or Appendix 3,
-which Licensee has purchased and which is provided to Licensee under the terms
-of this Agreement. Licensed Software shall include corresponding online or
-electronic documentation, associated media and printed materials, including the
-source code (where applicable), example programs and the documentation.
-Licensed Software does not include Third Party Software (as defined in Section
-4) or Open Source Qt. The Qt Company may, in the course of its development
-activities, at its free and absolute discretion and without any obligation to
-send or publish any notifications to the Licensee or in general, make changes,
-additions or deletions in the components and functionalities of the Licensed
-Software, provided that no such changes, additions or deletions will affect
-the already released version of the Licensed Software, but only upcoming
-version(s).
-
-"Licensee" shall mean the individual or legal entity that is party to this
-Agreement.
-
-"Licensee's Records" shall mean books and records that contain information
-bearing on Licensee's compliance with this Agreement, Licensee's use of Open
-Source Qt and/or the payments due to The Qt Company under this Agreement,
-including, but not limited to user information, assembly logs, sales records
-and distribution records.
-
-"Modified Software" shall have the meaning as set forth in Section 2.3.
-
-"Online Services" shall mean any services or access to systems made available
-by The Qt Company to the Licensee over the Internet relating to the Licensed
-Software or for the purpose of use by the Licensee of the Licensed Software or
-Support. Use of any such Online Services is discretionary for the Licensee and
-some of them may be subject to additional fees.
-
-"Open Source Qt" shall mean Qt Software available under the terms of the GNU
-Lesser General Public License, version 2.1 or later ("LGPL") or the GNU General
-Public License, version 2.0 or later ("GPL"). For clarity, Open Source Qt shall
-not be provided, governed or used under this Agreement.
-
-"Party" or "Parties" shall mean Licensee and/or The Qt Company.
-
-"Permitted Software" shall mean (i) third party open source software products
-that are generally available for public in source code form and free of any
-charge under any of the licenses approved by Open Source Initiative as listed
-on https://opensource.org/licenses, which may include parts of Open Source Qt
-or be developed using Open Source Qt; and (ii) software The Qt Company has made
-available via its Qt Marketplace online distribution channel.
-
-"Pre-Release Code" shall have the meaning as set forth in Section 4.
-
-"Prohibited Combination" shall mean any effort to use, combine, incorporate,
-link or integrate Licensed Software with any software created with or
-incorporating Open Source Qt, or use Licensed Software for creation of any such
-software.
-
-"Purchase Order" shall have the meaning as set forth in Section 10.2.
-
-"QA Tools" shall mean software libraries and tools as defined in Appendix 1
-depending on which product(s) the Licensee has purchased under the Agreement.
-
-"Qt Software" shall mean the software libraries and tools of The Qt Company,
-which The Qt Company makes available under commercial and/or open source
-licenses.
-
-"Redistributables" shall mean the portions of the Licensed Software set forth
-in Appendix 1 that may be distributed pursuant to the terms of this Agreement
-in object code form only, including any relevant documentation. Where relevant,
-any reference to Licensed Software in this Agreement shall include and refer
-also to Redistributables.
-
-"Renewal Term" shall mean an extension of previous Development License Term as
-agreed between the Parties.
-
-"Submitted Modified Software" shall have the meaning as set forth in Section
-2.3.
-
-"Support" shall mean standard developer support that is provided by The Qt
-Company to assist Designated Users in using the Licensed Software in accordance
-with this Agreement and the Support Terms.
-
-"Support Terms" shall mean The Qt Company's standard support terms specified in
-Appendix 9 hereto.
-
-"Taxes" shall have the meaning set forth in Section 10.5.
-
-"The Qt Company" shall mean:
- (i) in the event Licensee is an individual residing in the United States or
- a legal entity incorporated in the United States or having its
- headquarters in the United States, The Qt Company Inc., a Delaware
- corporation with its office at 3031 Tisch Way, 110 Plaza West,
- San Jose, CA 95128, USA.; or
- (ii) in the event the Licensee is an individual residing outside of the
- United States or a legal entity incorporated outside of the United
- States or having its registered office outside of the United States,
- The Qt Company Ltd., a Finnish company with its registered office at
- Miestentie 7, 02150 Espoo, Finland.
-
-"Third-Party Software" shall have the meaning set forth in Section 4.
-
-"Updates" shall mean a release or version of the Licensed Software containing
-bug fixes, error corrections and other changes that are generally made
-available to users of the Licensed Software that have contracted for Support.
-Updates are generally depicted as a change to the digits following the decimal
-in the Licensed Software version number. The Qt Company shall make Updates
-available to the Licensee under the Support. Updates shall be considered as
-part of the Licensed Software hereunder.
-
-"Upgrades" shall mean a release or version of the Licensed Software containing
-enhancements and new features and are generally depicted as a change to the
-first digit of the Licensed Software version number. In the event Upgrades are
-provided to the Licensee under this Agreement, they shall be considered as part
-of the Licensed Software hereunder.
-
-2. OWNERSHIP
-
-2.1. Ownership of The Qt Company
-
-The Licensed Software is protected by copyright laws and international
-copyright treaties, as well as other intellectual property laws and treaties.
-The Licensed Software is licensed, not sold.
-
-All of The Qt Company's Intellectual Property Rights are and shall remain the
-exclusive property of The Qt Company or its licensors respectively. No rights
-to The Qt Company's Intellectual Property Rights are assigned or granted to
-Licensee under this Agreement, except when and to the extent expressly
-specified herein.
-
-2.2. Ownership of Licensee
-
-All the Licensee's Intellectual Property Rights are and shall remain the
-exclusive property of the Licensee or its licensors respectively.
-
-All Intellectual Property Rights to the Modified Software, Applications and
-Devices shall remain with the Licensee and no rights thereto shall be granted
-by the Licensee to The Qt Company under this Agreement (except as set forth in
-Section 2.3 below).
-
-2.3. Modified Software
-
-Licensee may create bug-fixes, error corrections, patches or modifications to
-the Licensed Software ("Modified Software"). Such Modified Software may break
-the source or binary compatibility with the Licensed Software (including
-without limitation through changing the application programming interfaces
-("API") or by adding, changing or deleting any variable, method, or class
-signature in the Licensed Software and/or any inter-process protocols,
-services or standards in the Licensed Software libraries). To the extent that
-Licensee's Modified Software so breaks source or binary compatibility with the
-Licensed Software, Licensee acknowledges that The Qt Company's ability to
-provide Support may be prevented or limited and Licensee's ability to make use
-of Updates may be restricted.
-
-Licensee may, at its sole and absolute discretion, choose to submit Modified
-Software to The Qt Company ("Submitted Modified Software") in connection with
-Licensee's Support request, service request or otherwise. In the event
-Licensee does so, then, Licensee hereby grants The Qt Company a sublicensable,
-assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and
-fully paid-up license, under all of Licensee's Intellectual Property Rights, to
-reproduce, adapt, translate, modify, and prepare derivative works of, publicly
-display, publicly perform, sublicense, make available and distribute such
-Submitted Modified Software as The Qt Company sees fit at its free and absolute
-discretion.
-
-3. LICENSES GRANTED
-
-3.1. Development with Licensed Software
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for each Development
-License Term, to use, modify and copy the Licensed Software by Designated
-Users on the Development Platforms for the sole purposes of designing,
-developing, demonstrating and testing Application(s) and/or Devices, and to
-provide thereto related support and other related services to Customers. Each
-Application and/or Device can only include, incorporate or integrate
-contributions by such Designated Users who are duly licensed for the applicable
-Development Platform(s) and Deployment Platform(s) (i.e have a valid license
-for the appropriate Licensed Software product).
-
-Licensee may install copies of the Licensed Software on five (5) computers per
-Designated User, provided that only the Designated Users who have a valid
-Development License may use the Licensed Software.
-
-Licensee may at any time designate another Designated User to replace a
-then-current Designated User by notifying The Qt Company in writing, where such
-replacement is due to termination of employment, change of job duties, long
-time absence or other such permanent reason affecting Designated User's need
-for Licensed Software.
-
-Upon expiry of the initially agreed Development License Term, the respective
-Development License Term shall be automatically extended to one or more Renewal
-Term(s), unless and until either Party notifies the other Party in writing, or
-any other method acceptable to The Qt Company (it being specifically
-acknowledged and understood that verbal notification is explicitly deemed
-inadequate in all circumstances), that it does not wish to continue the
-Development License Term, such notification to be provided to the other Party
-no less than thirty (30) days before expiry of the respective Development
-License Term. The Qt Company shall, in good time before the due date for the
-above notification, remind the Licensee on the coming Renewal Term. Unless
-otherwise agreed between the Parties, Renewal Term shall be 12 months.
-
-Any such Renewal Term shall be subject to License Fees agreed between the
-Parties or, if no advance agreement exists, subject to The Qt Company's
-standard list pricing applicable at the commencement date of any such
-Renewal Term.
-
-The Qt Company may either request the Licensee to place a purchase order
-corresponding to a quote by The Qt Company, or use Licensee's stored Credit
-Card information in the Qt Account to automatically charge the Licensee for the
-relevant Renewal Term.
-
-3.2. Distribution of Applications
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
-this Agreement), right and license, valid for the Agreement Term, to
- (i) distribute, by itself or through its Contractors, Redistributables as
- installed, incorporated or integrated into Applications for execution
- on the Deployment Platforms, and
- (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
- distributed hereunder, for Customers solely to the extent necessary in
- order for the Customers to use the Applications for their respective
- intended purposes.
-
-Right to distribute the Redistributables as part of an Application as provided
-herein is not royalty-bearing but is conditional upon the Application having
-been created, updated and maintained under a valid and duly paid Development
-Licenses.
-
-3.3. Distribution of Devices
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
-this Agreement), right and license, valid for the Agreement Term, to
- (i) distribute, by itself or through one or more tiers of Contractors,
- Redistributables as installed, incorporated or integrated, or intended
- to be installed, incorporated or integrated into Devices for execution
- on the Deployment Platforms, and
- (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
- distributed hereunder, for Customers solely to the extent necessary in
- order for the Customers to use the Devices for their respective
- intended purposes.
-
-Right to distribute the Devices as provided herein is conditional upon
- (i) the Devices having been created, updated and maintained under a valid
- and duly paid Development Licenses, and
- (ii) the Licensee having acquired corresponding Distribution Licenses at
- the time of distribution of any Devices to Customers.
-
-3.4. Further Requirements
-
-The licenses granted above in this Section 3 by The Qt Company to Licensee are
-conditional and subject to Licensee's compliance with the following terms:
- (i) Licensee acknowledges that The Qt Company has separate products of
- Licensed Software for the purpose of Applications and Devices
- respectively, where development and distribution of Devices is only
- allowed using the correct designated product. Licensee shall make sure
- and bear the burden of proof that Licensee is using a correct product
- of Licensed Software entitling Licensee to development and distribution
- of Devices;
- (ii) Licensee shall not remove or alter any copyright, trademark or other
- proprietary rights notice(s) contained in any portion of the Licensed
- Software;
- (iii) Applications must add primary and substantial functionality to the
- Licensed Software so as not to compete with the Licensed Software;
- (iv) Applications may not pass on functionality which in any way makes it
- possible for others to create software with the Licensed Software;
- provided however that Licensee may use the Licensed Software's
- scripting and QML ("Qt Quick") functionality solely in order to enable
- scripting, themes and styles that augment the functionality and
- appearance of the Application(s) without adding primary and substantial
- functionality to the Application(s);
- (v) Licensee shall not use Licensed Software in any manner or for any
- purpose that infringes, misappropriates or otherwise violates any
- Intellectual property or right of any third party, or that violates any
- applicable law;
- (vi) Licensee shall not use The Qt Company's or any of its suppliers'
- names, logos, or trademarks to market Applications, except that
- Licensee may use "Built with Qt" logo to indicate that Application(s)
- or Device(s) was developed using the Licensed Software;
- (vii) Licensee shall not distribute, sublicense or disclose source code of
- Licensed Software to any third party (provided however that Licensee
- may appoint employee(s) of Contractors and Affiliates as Designated
- Users to use Licensed Software pursuant to this Agreement). Such right
- may be available for the Licensee subject to a separate software
- development kit ("SDK") license agreement to be concluded with The Qt
- Company;
- (viii) Licensee shall not grant the Customers a right to (a) make copies of
- the Redistributables except when and to the extent required to use the
- Applications and/or Devices for their intended purpose, (b) modify the
- Redistributables or create derivative works thereof, (c) decompile,
- disassemble or otherwise reverse engineer Redistributables, or (d)
- redistribute any copy or portion of the Redistributables to any third
- party, except as part of the onward sale of the Application or Device
- on which the Redistributables are installed;
- (ix) Licensee shall not and shall cause that its Affiliates or Contractors
- shall not use Licensed Software in any Prohibited Combination, unless
- Licensee has received an advance written permission from The Qt Company
- to do so. Absent such written permission, any and all distribution by
- the Licensee during the Agreement Term of a hardware device or product
- a) which incorporate or integrate any part of Licensed Software or Open
- Source Qt; or b) where substantial functionality is provided by
- software built with Licensed Software or Open Source Qt or otherwise
- depends on the Licensed Software or Open Source Qt, shall be considered
- to be Device distribution under this Agreement and shall be dependent
- on Licensee's compliance thereof (including but not limited to
- obligation to pay applicable License Fees for such distribution).
- Notwithstanding what is provided above in this sub-section (ix),
- Licensee is entitled to use and combine Licensed Software with any
- Permitted Software;
- (x) Licensee shall cause all of its Affiliates, Contractors and Customers
- entitled to make use of the licenses granted under this Agreement, to
- be contractually bound to comply with the relevant terms of this
- Agreement and not to use the Licensed Software beyond the terms hereof
- and for any purposes other than operating within the scope of their
- services for Licensee. Licensee shall be responsible for any and all
- actions and omissions of its Affiliates and Contractors relating to the
- Licensed Software and use thereof (including but not limited to payment
- of all applicable License Fees);
- (xi) Except when and to the extent explicitly provided in this Section 3,
- Licensee shall not transfer, publish, disclose, display or otherwise
- make available the Licensed Software; and
- (xii) Licensee shall not attempt or enlist a third party to conduct or
- attempt to conduct any of the above.
-
-Above terms shall not be applicable if and to the extent they conflict with any
-mandatory provisions of any applicable laws.
-
-Any use of Licensed Software beyond the provisions of this Agreement is
-strictly prohibited and requires an additional license from The Qt Company.
-
-3.5 QA Tools License
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for the Development
-License Term, to use the QA Tools for Licensee's internal business purposes in
-the manner provided below and in Appendix 1 hereto.
-
-Licensee may modify the QA Tools except for altering or removing any details of
-ownership, copyright, trademark or other property right connected with the QA
-Tools.
-
-Licensee shall not distribute the QA Tools or any part thereof, modified or
-unmodified, separately or as part of any software package, Application or
-Device.
-
-Upon expiry of the initially agreed Development License Term, the respective
-Development License Term shall be automatically extended to one or more Renewal
-Term(s), unless and until either Party notifies the other Party in writing, or
-any other method acceptable to The Qt Company (it being specifically
-acknowledged and understood that verbal notification is explicitly deemed
-inadequate in all circumstances), that it does not wish to continue the
-Development License Term, such notification to be provided to the other Party
-no less than thirty (30) days before expiry of the respective Development
-License Term. The Qt Company shall, in good time before the due date for the
-above notification, remind the Licensee on the coming Renewal Term. Unless
-otherwise agreed between the Parties, Renewal Term shall be 12 months.
-
-Any such Renewal Term shall be subject to License Fees agreed between the
-Parties or, if no advance agreement exists, subject to The Qt Company's
-standard list pricing applicable at the commencement date of any such
-Renewal Term.
-
-3.6 Evaluation License
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for the Evaluation
-License Term to use the Licensed Software solely for the Licensee's internal
-use to evaluate and determine whether the Licensed Software meets Licensee's
-business requirements, specifically excluding any commercial use of the
-Licensed Software or any derived work thereof.
-
-Upon the expiry of the Evaluation License Term, Licensee must either
-discontinue use of the relevant Licensed Software or acquire a commercial
-Development License or QA Tools License specified herein.
-
-4. THIRD-PARTY SOFTWARE
-
-The Licensed Software may provide links or access to third party libraries or
-code (collectively "Third-Party Software") to implement various functions.
-Third-Party Software does not, however, comprise part of the Licensed Software,
-but is provided to Licensee complimentary and use thereof is discretionary for
-the Licensee. Third-Party Software will be listed in the ".../src/3rdparty"
-source tree delivered with the Licensed Software or documented in the Licensed
-Software, as such may be amended from time to time. Licensee acknowledges that
-use or distribution of Third-Party Software is in all respects subject to
-applicable license terms of applicable third-party right holders.
-
-5. PRE-RELEASE CODE
-
-The Licensed Software may contain pre-release code and functionality, or sample
-code marked or otherwise stated with appropriate designation such as
-"Technology Preview", "Alpha", "Beta", "Sample", "Example" etc.
-("Pre-Release Code").
-
-Such Pre-Release Code may be present complimentary for the Licensee, in order
-to provide experimental support or information for new platforms or
-preliminary versions of one or more new functionalities or for other similar
-reasons. The Pre-Release Code may not be at the level of performance and
-compatibility of a final, generally available, product offering. The
-Pre-Release Code may not operate correctly, may contain errors and may be
-substantially modified by The Qt Company prior to the first commercial
-product release, if any. The Qt Company is under no obligation to make
-Pre-Release Code commercially available, or provide any Support or Updates
-relating thereto. The Qt Company assumes no liability whatsoever regarding
-any Pre-Release Code, but any use thereof is exclusively at Licensee's own risk
-and expense.
-
-For clarity, unless Licensed Software specifies different license terms for the
-respective Pre-Release Code, the Licensee is entitled to use such pre-release
-code pursuant to Section 3, just like other Licensed Software.
-
-6. LIMITED WARRANTY AND WARRANTY DISCLAIMER
-
-The Qt Company hereby represents and warrants that (i) it has the power and
-authority to grant the rights and licenses granted to Licensee under this
-Agreement, and (ii) Licensed Software will operate materially in accordance
-with its specifications.
-
-Except as set forth above, the Licensed Software is licensed to Licensee "as
-is" and Licensee's exclusive remedy and The Qt Company's entire liability for
-errors in the Licensed Software shall be limited, at The Qt Company's option,
-to correction of the error, replacement of the Licensed Software or return of
-the applicable fees paid for the defective Licensed Software for the time
-period during which the License is not able to utilize the Licensed Software
-under the terms of this Agreement.
-
-TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF
-ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER
-WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
-NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT
-WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE'S REQUIREMENTS OR THAT
-IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE
-UNINTERRUPTED.
-
-7. LIMITATION OF LIABILITY
-
-EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
-BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
-EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT,
-LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL,
-CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND,
-HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT.
-
-EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
-BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
-EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT
-EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT COMPANY BY LICENSEE
-DURING THE DEVELOPMENT LICENSE TERM DURING WHICH THE EVENT RESULTING IN SUCH
-LIABILITY OCCURRED.
-
-THE PROVISIONS OF THIS SECTION 7 ALLOCATE THE RISKS UNDER THIS AGREEMENT
-BETWEEN THE QT COMPANY AND LICENSEE AND THE PARTIES HAVE RELIED UPON THE
-LIMITATIONS SET FORTH HEREIN IN DETERMINING WHETHER TO ENTER INTO THIS
-AGREEMENT.
-
-NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THIS AGREEMENT, LICENSEE SHALL
-ALWAYS BE LIABLE TO PAY THE APPLICABLE LICENSE FEES CORRESPONDING TO ITS
-ACTUAL USE OF LICENSED SOFTWARE.
-
-8. SUPPORT, UPDATES AND ONLINE SERVICES
-
-Upon due payment of the agreed License Fees the Licensee will be eligible to
-receive Support and Updates and to use the Online Services during the agreed
-Development License Term or other agreed fixed time period. Support is
-provided according to agreed support level and subject to applicable
-requirements and restrictions, as specified in the Support Terms.
-
-Unless otherwise decided by The Qt Company at its free and absolute discretion,
-Upgrades will not be included in the Support but may be available subject to
-additional fees.
-
-From time to time The Qt Company may change the Support Terms, provided that
-during the respective ongoing Support period the level of Support may not be
-reduced without the consent of the Licensee.
-
-Unless otherwise agreed, The Qt Company shall not be responsible for providing
-any service or support to Customers.
-
-9. CONFIDENTIALITY
-
-Each Party acknowledges that during the Agreement Term each Party may receive
-information about the other Party's business, business methods, business plans,
-customers, business relations, technology, and other information, including the
-terms of this Agreement, that is confidential and of great value to the other
-Party, and the value of which would be significantly reduced if disclosed to
-third parties ("Confidential Information"). Accordingly, when a Party (the
-"Receiving Party") receives Confidential Information from the other Party (the
-"Disclosing Party"), the Receiving Party shall only disclose such information
-to employees and Contractors on a need to know basis, and shall cause its
-employees and employees of its Affiliates to: (i) maintain any and all
-Confidential Information in confidence; (ii) not disclose the Confidential
-Information to a third party without the Disclosing Party's prior written
-approval; and (iii) not, directly or indirectly, use the Confidential
-Information for any purpose other than for exercising its rights and
-fulfilling its responsibilities pursuant to this Agreement. Each Party shall
-take reasonable measures to protect the Confidential Information of the other
-Party, which measures shall not be less than the measures taken by such Party
-to protect its own confidential and proprietary information.
-
-Obligation of confidentiality shall not apply to information that (i) is or
-becomes generally known to the public through no act or omission of the
-Receiving Party; (ii) was in the Receiving Party's lawful possession prior to
-the disclosure hereunder and was not subject to limitations on disclosure or
-use; (iii) is developed independently by employees or Contractors of the
-Receiving Party or other persons working for the Receiving Party who have not
-had access to the Confidential Information of the Disclosing Party, as proven
-by the written records of the Receiving Party; (iv) is lawfully disclosed to
-the Receiving Party without restrictions, by a third party not under an
-obligation of confidentiality; or (v) the Receiving Party is legally compelled
-to disclose, in which case the Receiving Party shall notify the Disclosing
-Party of such compelled disclosure and assert the privileged and confidential
-nature of the information and cooperate fully with the Disclosing Party to
-limit the scope of disclosure and the dissemination of disclosed Confidential
-Information to the minimum extent necessary.
-
-The obligations under this Section 9 shall continue to remain in force for a
-period of five (5) years after the last disclosure, and, with respect to trade
-secrets, for so long as such trade secrets are protected under applicable trade
-secret laws.
-
-10. FEES, DELIVERY AND PAYMENT
-
-10.1. License Fees
-
-License Fees are described in The Qt Company's standard price list, quote or
-Purchase Order confirmation or in an Appendix 2 hereto, as the case may be.
-
-Unless otherwise expressly provided in this Agreement, the License Fees shall
-not be refunded or claimed as a credit in any event or for any reason
-whatsoever.
-
-10.2. Ordering Licenses
-
-Licensee may purchase Development Licenses, Distribution Licenses and QA Tools
-Licenses pursuant to agreed pricing terms or, if no specific pricing terms have
-been agreed upon, at The Qt Company's standard pricing terms applicable at the
-time of purchase.
-
-Unless expressly otherwise agreed, any price or other term quoted to the
-Licensee or specified herein shall only be valid for the thirty (30) days from
-the effective date of this Agreement, Appendix 2 or the date of the quote, as
-applicable.
-
-Licensee shall submit all purchase orders for Development Licenses and
-Distribution Licenses to The Qt Company by email or any other method acceptable
-to The Qt Company (each such order is referred to herein as a "Purchase Order")
-for confirmation, whereupon the Purchase Order shall become binding between the
-Parties.
-
-Licensee acknowledges and agrees that all Purchase Orders for Licensed Software
-the Licensee makes during the Agreement Term shall be governed exclusively
-under the terms of this Agreement.
-
-10.3. Distribution License Packs
-
-Unless otherwise agreed, Distribution Licenses shall be purchased by way of
-Distribution License Packs.
-
-Upon due payment of the ordered Distribution License Pack(s), the Licensee will
-have an account of Distribution Licenses available for distributing the
-Redistributables in accordance with this Agreement.
-
-Each time Licensee distributes a copy of Redistributables, then one
-Distribution License is used, and Licensee's account of available Distribution
-Licenses is decreased accordingly.
-
-Licensee may distribute copies of the Redistributables so long as Licensee has
-Distribution Licenses remaining on its account.
-
-10.4. Payment Terms
-
-License Fees and any other charges under this Agreement shall be paid by
-Licensee no later than thirty (30) days from the date of the applicable invoice
-from The Qt Company.
-
-The Qt Company will submit an invoice to Licensee after the date of this
-Agreement and/or after The Qt Company receives a Purchase Order from Licensee.
-
-A late payment charge of the lower of (a) one percent per month; or (b) the
-interest rate stipulated by applicable law, shall be charged on any unpaid
-balances that remain past due and which have not been disputed by the Licensee
-in good faith.
-
-10.5. Taxes
-
-All License Fees and other charges payable hereunder are gross amounts but
-exclusive of any value added tax, use tax, sales tax, withholding tax and other
-taxes, duties or tariffs ("Taxes") levied directly for the sale, delivery or
-use of Licensed Software hereunder pursuant to any applicable law. Such
-applicable Taxes shall be paid by Licensee to The Qt Company, or, where
-applicable, in lieu of payment of such Taxes to The Qt Company, Licensee shall
-provide an exemption certificate to The Qt Company and any applicable
-authority.
-
-11. RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS
-
-11.1. Licensee's Record-keeping
-
-Licensee shall at all times during the Agreement Term and for a period of two
-(2) years thereafter maintain Licensee's Records in an accurate and up-to-date
-form. Licensee's Records shall be adequate to reasonably enable The Qt Company
-to determine Licensee's compliance with the provisions of this Agreement. The
-records shall conform to general good accounting practices.
-
-Licensee shall, within thirty (30) days from receiving The Qt Company's request
-to that effect, deliver to The Qt Company a report based on Licensee's Records,
-such report to contain information, in sufficient detail, on (i) number and
-identity of users working with Licensed Software or Open Source Qt, (ii) copies
-of Redistributables distributed by Licensee during the most recent calendar
-quarter and/or any other term specified by The Qt Company, , and (iii) any
-other information pertaining to Licensee's compliance with the terms of this
-Agreement (like e.g. information on products and/or projects relating to use of
-Distribution Licenses), as The Qt Company may reasonably require from time to
-time.
-
-11.2. The Qt Company's Audit Rights
-
-The Qt Company or an independent auditor acting on behalf of The Qt Company's,
-may, upon at least thirty (30) days' prior written notice and at its expense,
-audit Licensee with respect to the Licensee's use of the Licensed Software, but
-not more frequently than once during each 6-month period. Such audit may be
-conducted by mail, electronic means or through an in-person visit to Licensee's
-place of business. Any possible in-person audit shall be conducted during
-regular business hours at Licensee's facilities and shall not unreasonably
-interfere with Licensee's business activities and shall be limited in scope to
-verify Licensee's compliance with the terms of this Agreement. The Qt Company
-or the independent auditor acting on behalf of The Qt Company shall be entitled
-to inspect Licensee's Records and conduct necessary interviews of Licensee's
-relevant employees and Contractors. All such Licensee's Records and use thereof
-shall be subject to an obligation of confidentiality under this Agreement.
-
-If an audit reveals that Licensee is using the Licensed Software beyond scope
-of the licenses Licensee has paid for, Licensee shall pay to The Qt Company any
-amounts owed for such unauthorized use within 30 days from receipt of the
-corresponding invoice from The Qt Company.
-
-In addition, in the event the audit reveals a material violation of the terms
-of this Agreement (without limitation, either (i) underpayment of more than 10
-% of License Fees or 10,000 euros (whichever is more) or (ii) distribution of
-products, which include or result from Prohibited Combination, shall be deemed
-a material violation for purposes of this section), then the Licensee shall
-pay The Qt Company's reasonable cost of conducting such audit.
-
-12. TERM AND TERMINATION
-
-12.1. Agreement Term
-
-This Agreement shall enter into force upon due acceptance by both Parties and
-remain in force until terminated pursuant to the terms of this Section 12
-("Agreement Term").
-
-12.2. Termination for breach and suspension of rights
-Either Party shall have the right to terminate this Agreement upon thirty (30)
-days prior written notice if the other Party commits a material breach of any
-obligation of this Agreement and fails to remedy such breach within such notice
-period.
-
-Instead of termination, The Qt Company shall have the right to suspend or
-withhold grants of all rights to the Licensed Software hereunder, including but
-not limited to the Development Licenses, Distribution License, and Support,
-should Licensee fail to make payment in timely fashion or otherwise violates or
-is reasonably suspected to violate its obligations or terms of this Agreement,
-and where such violation or breach is not cured within ten (10) business days
-following The Qt Company's written notice thereof.
-
-12.3. Termination for insolvency
-
-Either Party shall have the right to terminate this Agreement immediately upon
-written notice in the event that the other Party becomes insolvent, files for
-any form of bankruptcy, makes any assignment for the benefit of creditors, has
-a receiver, administrative receiver or officer appointed over the whole or a
-substantial part of its assets, ceases to conduct business, or an act
-equivalent to any of the above occurs under the laws of the jurisdiction of the
-other Party.
-
-12.4. Parties' Rights and Duties upon Termination
-
-Upon expiry or termination of the Agreement, Licensee shall cease and shall
-cause all Designated Users (including those of its Affiliates' and
-Contractors') to cease using the Licensed Software under this Agreement. For
-clarity, a Development License of a Designated User or a QA Tools License, and
-all rights relating thereto, shall always terminate at the expiry of the
-respective Development License Term, even if the Agreement continues to remain
-in force.
-
-Upon such termination the Licensee shall destroy or return to The Qt Company
-all copies of the Licensed Software and all related materials and will certify
-the same by Licensee's duly authorized officer to The Qt Company upon its
-request, provided however that Licensee may retain and exploit such copies of
-the Licensed Software as it may reasonably require in providing continued
-support to Customers.
-
-Except when this Agreement is terminated by The Qt Company due to Licensee's
-material breach as set forth in Section 12.2, the Licensee may continue
-distribution of Applications and Devices under the terms of this Agreement
-despite the termination of this Agreement. In such event the terms hereof will
-continue to be applicable and govern any such distribution of Applications and
-Devices beyond the expiry or termination of this Agreement. In case of
-termination by The Qt Company due to Licensee's material breach, Licensee must
-cease any distribution of Applications and Devices at the date of termination
-of this Agreement.
-
-Expiry or termination of this Agreement for any reason whatsoever shall not
-relieve Licensee of its obligation to pay any License Fees accrued or payable
-to The Qt Company prior to the effective date of termination, and Licensee pay
-to The Qt Company all such fees within 30 days from the effective date of
-termination of this Agreement.
-
-Termination of this Agreement shall not affect any rights of Customers to
-continue use of Applications and Devices (and therein incorporated
-Redistributables).
-
-12.5. Extension of Rights under Special Circumstances
-
-In the event of The Qt Company choosing not to renew the Development License(s)
-or QA Tools Licenses, as set forth in Section 3.1 and 3.5 respectively, and
-where such decision of non-renewal is not due to any ongoing breach or alleged
-breach (as reasonably determined by The Qt Company) by Licensee of the terms of
-this Agreement or any applicable license terms of Open Source Qt, then all
-valid and affected Development Licenses and QA Tools licenses possessed by the
-Licensee at such date shall be extended to be valid in perpetuity under the
-terms of this Agreement and Licensee is entitled to purchase additional
-licenses as set forth in Section 10.2.
-
-In the event The Qt Company is declared bankrupt under a final, non-cancellable
-decision by relevant court of law, and this Agreement is not, at the date of
-expiry of the Development License(s) or QA Tools Licenses, assigned to party,
-who has assumed The Qt Company's position as a legitimate licensor of Licensed
-Software under this Agreement, then all valid Development Licenses and QA Tools
-Licenses possessed by the Licensee at such date of expiry, and which the
-Licensee has not notified for expiry, shall be extended to be valid in
-perpetuity under the terms of this Agreement.
-
-For clarity, in case of an extension under this Section 12.5, any such
-extension shall not apply to The Qt Company's Support obligations, but Support
-shall be provided only up until the end of the respective fixed Development
-License Term regardless of the extension of relevant Development License or QA
-Tools License, unless otherwise agreed between the Parties.
-
-13. GOVERNING LAW AND LEGAL VENUE
-
-In the event this Agreement is in the name of The Qt Company Inc., a Delaware
-Corporation, then:
- (i) this Agreement shall be construed and interpreted in accordance with
- the laws of the State of California, USA, excluding its choice of law
- provisions;
- (ii) the United Nations Convention on Contracts for the International Sale
- of Goods will not apply to this Agreement; and
- (iii) any dispute, claim or controversy arising out of or relating to this
- Agreement or the breach, termination, enforcement, interpretation or
- validity thereof, including the determination of the scope or
- applicability of this Agreement to arbitrate, shall be determined by
- arbitration in San Francisco, USA, before one arbitrator. The
- arbitration shall be administered by JAMS pursuant to JAMS' Streamlined
- Arbitration Rules and Procedures. Judgment on the Award may be entered
- in any court having jurisdiction. This Section shall not preclude
- parties from seeking provisional remedies in aid of arbitration from a
- court of appropriate jurisdiction.
-
-In the event this Agreement is in the name of The Qt Company Ltd., a Finnish
-Company, then:
- (i) this Agreement shall be construed and interpreted in accordance with
- the laws of Finland, excluding its choice of law provisions;
- (ii) the United Nations Convention on Contracts for the International Sale
- of Goods will not apply to this Agreement; and
- (iii) any disputes, controversy or claim arising out of or relating to this
- Agreement, or the breach, termination or validity thereof shall be
- finally settled by arbitration in accordance with the Arbitration Rules
- of International Chamber of Commerce. The arbitration tribunal shall
- consist of one (1), or if either Party so requires, of three (3),
- arbitrators. The award shall be final and binding and enforceable in
- any court of competent jurisdiction. The arbitration shall be held in
- Helsinki, Finland and the process shall be conducted in the English
- language. This Section shall not preclude parties from seeking
- provisional remedies in aid of arbitration from a court of appropriate
- jurisdiction.
-
-14. GENERAL PROVISIONS
-
-14.1. No Assignment
-
-Except in the case of a merger or sale of substantially all of its corporate
-assets, Licensee shall not be entitled to assign or transfer all or any of its
-rights, benefits and obligations under this Agreement without the prior written
-consent of The Qt Company, which shall not be unreasonably withheld or delayed.
-The Qt Company shall be entitled to freely assign or transfer any of its
-rights, benefits or obligations under this Agreement.
-
-14.2. No Third-Party Representations
-
-Licensee shall make no representations or warranties concerning the Licensed
-Software on behalf of The Qt Company. Any representation or warranty Licensee
-makes or purports to make on The Qt Company's behalf shall be void as to
-The Qt Company.
-
-14.3. Surviving Sections
-
-Any terms and conditions that by their nature or otherwise reasonably should
-survive termination of this Agreement shall so be deemed to survive. Such
-sections include especially the following: 1, 2, 6, 7, 9, 11, 12.4, 13 and 14.
-
-14.4. Entire Agreement
-
-This Agreement, the Appendices hereto, the License Certificate and any
-applicable quote and Purchase Order accepted by The Qt Company constitute the
-complete agreement between the Parties and supersedes all prior or
-contemporaneous discussions, representations, and proposals, written or oral,
-with respect to the subject matters discussed herein.
-
-In the event of any conflict or inconsistency between this Agreement and any
-Purchase Order, the terms of this Agreement will prevail over the terms of the
-Purchase Order with respect to such conflict or inconsistency.
-
-Parties specifically acknowledge and agree that this Agreement prevails over
-any click-to-accept or similar agreements the Designated Users may need to
-accept online upon download of the Licensed Software, as may be required by
-The Qt Company's applicable processes relating to Licensed Software.
-
-14.5. Modifications
-
-No modification of this Agreement shall be effective unless contained in a
-writing executed by an authorized representative of each Party. No term or
-condition contained in Licensee's Purchase Order ("Deviating Terms") shall
-apply unless The Qt Company has expressly agreed such Deviating Terms in
-writing. Unless and to the extent expressly agreed by The Qt Company, any such
-Deviating Terms shall be deemed void and with no legal effect. For clarity,
-delivery of the Licensed Software following the receipt of the Purchase Order
-including Deviating Terms shall not constitute acceptance of such Deviating
-Terms.
-
-14.6. Force Majeure
-
-Except for the payment obligations hereunder, neither Party shall be liable to
-the other for any delay or non-performance of its obligations hereunder in the
-event and to the extent that such delay or non-performance is due to an event
-of act of God, terrorist attack or other similar unforeseeable catastrophic
-event that prevents either Party for fulfilling its obligations under this
-Agreement and which such Party cannot avoid or circumvent ("Force Majeure
-Event"). If the Force Majeure Event results in a delay or non-performance of a
-Party for a period of three (3) months or longer, then either Party shall have
-the right to terminate this Agreement with immediate effect without any
-liability (except for the obligations of payment arising prior to the event of
-Force Majeure) towards the other Party.
-
-14.7. Notices
-
-Any notice given by one Party to the other shall be deemed properly given and
-deemed received if specifically acknowledged by the receiving Party in writing
-or when successfully delivered to the recipient by hand, fax, or special
-courier during normal business hours on a business day to the addresses
-specified for each Party on the signature page. Each communication and document
-made or delivered by one Party to the other Party pursuant to this Agreement
-shall be in the English language.
-
-14.8. Export Control
-
-Licensee acknowledges that the Redistributables, as incorporated in
-Applications or Devices, may be subject to export control restrictions under
-the applicable laws of respective countries. Licensee shall fully comply with
-all applicable export license restrictions and requirements as well as with all
-laws and regulations relating to the Redistributables and exercise of licenses
-hereunder and shall procure all necessary governmental authorizations,
-including without limitation, all necessary licenses, approvals, permissions or
-consents, where necessary for the re-exportation of the Redistributables,
-Applications and/or Devices.
-
-14.9. No Implied License
-
-There are no implied licenses or other implied rights granted under this
-Agreement, and all rights, save for those expressly granted hereunder, shall
-remain with The Qt Company and its licensors. In addition, no licenses or
-immunities are granted to the combination of the Licensed Software with any
-other software or hardware not delivered by The Qt Company under this
-Agreement.
-
-14.10. Attorney Fees
-
-The prevailing Party in any action to enforce this Agreement shall be entitled
-to recover its attorney's fees and costs in connection with such action, as to
-be ordered by the relevant dispute resolution body.
-
-14.11. Privacy
-
-Licensee acknowledges and agrees that for the purpose of this Agreement,
-The Qt Company may collect, use, transfer and disclose personal data pertaining
-to Designated Users as well as any other employees and directors of the
-Licensee and its Contractors relevant for carrying out the intent of this
-Agreement. Such personal data will be primarily collected from the relevant
-individuals but may be collected also from Licensee (e.g. in the course of
-Licensee's reporting obligations). The Parties acknowledge that as
-The Qt Company determines the purpose and means for such collection and
-processing of the applicable personal data, The Qt Company shall be regarded as
-the Data Controller under the applicable Data Protection Legislation.
-The Qt Company shall process any such personal data in accordance with its
-privacy and security policies and practices, which will comply with all
-applicable requirements of the Data Protection Legislation.
-
-14.12. Severability
-
-If any provision of this Agreement shall be adjudged by any court of competent
-jurisdiction to be unenforceable or invalid, that provision shall be limited or
-eliminated to the minimum extent necessary so that this Agreement shall
-otherwise remain in full force and effect and enforceable.
-
-14.13. Marketing Rights
-
-Parties have agreed upon Marketing Rights pursuant to Appendix 7, if any.
-
-
-
-
-APPENDICES
-The Agreement includes following Appendices 1-10, as applicable.
-- Appendix 1: Licensed Software details
-- Appendix 2: Pricing
-- Appendix 3: Add-on Software details (optional)
-- Appendix 4: Small business and startup Licenses (optional)
-- Appendix 5: Non-commercial and educational Licenses (optional)
-- Appendix 6: License Reporting (optional)
-- Appendix 7: Marketing Rights (optional)
-- Appendix 8: Intentionally left blank (optional)
-- Appendix 9: Support Terms
-- Appendix 10: Conversion from legacy Licenses to Subscription (optional)
-- Appendix 11: TERMS OF USE - QT INSIGHT TRACKER LIBRARY
-
-
-APPENDIX 1: LICENSED SOFTWARE
-
-The modules and/or tools that are included in the latest publicly available
-version of the respective product at the effective date of this Agreement- Qt
-for Application Development Professional (ADP), Qt for Application Development
-Enterprise (ADE), Qt for Device Creation Professional (DCP), Qt for Device
-Creation Enterprise (DCE), - are marked with "X" in the below table. The
-modules and tools are specific to each product version respectively and may
-vary from version to version. Modules and tools included in the latest publicly
-available version of the respective product at any given time are listed in
-Appendix 1 of the latest version of this Agreement available at
-www.qt.io/terms-conditions/. If a new version of Licensed Software does not
-include a module or tool present in an older version which Licensee is entitled
-to use under a valid license from The Qt Company, then Licensee will continue
-to have such right during the Term of this Agreement. In the event a new
-version of the Licensed Software adds modules or tools to any previous
-version(s), Licensee's rights will extend to cover also such additional modules
-and tools.
-
-Parts of the product that are permitted for distribution in object-code form
-only ("Redistributables") are marked with "R" in the below table.
-
-+----------------------------------------------------------+
-| Modules / Tools | ADP | ADE | DCP | DCE |
-+----------------------------------------------------------+
-| Active Qt | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt 3D | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt 5 Core Compatibility APIs | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Android Extras | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Bluetooth | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Canvas 3D | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Charts | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Concurrent | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Core | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Data Visualization | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt D-Bus | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt for Python | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt for WebAssembly | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Gamepad | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Graphical Effects | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt GUI | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Help | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Image Formats | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Location | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Lottie Animation | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Mac Extras | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Multimedia | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Multimedia Widgets | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Network | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Network Authorization | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt NFC | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OpenGL | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt PDF | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Platform Headers | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Positioning | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Print Support | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Purchasing | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt QML | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick 3D | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Controls 1 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Controls | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Dialogs | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Extras | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Layouts | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Test | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Timeline | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick WebGL | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Widgets | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Remote Objects | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Script | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Script Tools | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SCXML | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Sensors | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Serial Bus | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Serial Port | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Shader Tools | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Speech | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt State Machine | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SQL | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SVG | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Test | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt UI Tools | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Virtual Keyboard | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Wayland Compositor | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebChannel | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebEngine | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebSockets | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebView | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Widgets | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Windows Extras | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt X11 Extras | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt XML | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt XML Patterns | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Designer (Qt Widget Designer) | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Linguist | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Assistant | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lupdate | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lrelease | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lconvert | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt MQTT | | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt KNX | | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OPC UA | | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt CoAP | | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Boot 2 Qt stacks | | | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OTA | | | X,R | X,R |
-+----------------------------------------------------------+
-| Device Utilities | | | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Debugging Bridge (QDB) Daemon | | | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Ultralite Controls | | | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Ultralite | | | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Safe Renderer (QSR) | | | | X,R |
-+----------------------------------------------------------+
-| Qt Application Manager | | | | X,R |
-+----------------------------------------------------------+
-| Qt Interface Framework | | | | X,R |
-+----------------------------------------------------------+
-| Neptune Reference UI | | | | X,R |
-+----------------------------------------------------------+
-| Qt for Android Automotive (QAA) | | | | X,R |
-+----------------------------------------------------------+
-| Qt Creator | X | X | X | X |
-+----------------------------------------------------------+
-| Qt Design Studio Professional | X | X | X | X |
-+----------------------------------------------------------+
-| androiddeployqt | X | X | X | X |
-+----------------------------------------------------------+
-| androidtestrunner | X | X | X | X |
-+----------------------------------------------------------+
-| canbusutil | X | X | X | X |
-+----------------------------------------------------------+
-| dumpcpp | X | X | X | X |
-+----------------------------------------------------------+
-| dumpdoc | X | X | X | X |
-+----------------------------------------------------------+
-| fixqt4headers.pl | X | X | X | X |
-+----------------------------------------------------------+
-| idc | X | X | X | X |
-+----------------------------------------------------------+
-| moc | X | X | X | X |
-+----------------------------------------------------------+
-| pixeltool | X | X | X | X |
-+----------------------------------------------------------+
-| qdbus | X | X | X | X |
-+----------------------------------------------------------+
-| qdbuscpp2xml | X | X | X | X |
-+----------------------------------------------------------+
-| qdbusviwer | X | X | X | X |
-+----------------------------------------------------------+
-| qdbusxml2cpp | X | X | X | X |
-+----------------------------------------------------------+
-| qdistancefieldgenerator | X | X | X | X |
-+----------------------------------------------------------+
-| qdoc | X | X | X | X |
-+----------------------------------------------------------+
-| qhelpgenerator | X | X | X | X |
-+----------------------------------------------------------+
-| qlalr | X | X | X | X |
-+----------------------------------------------------------+
-| qmake | X | X | X | X |
-+----------------------------------------------------------+
-| qml | X | X | X | X |
-+----------------------------------------------------------+
-| qmlcachegen | X | X | X | X |
-+----------------------------------------------------------+
-| qmldom | X | X | X | X |
-+----------------------------------------------------------+
-| qmleasing | X | X | X | X |
-+----------------------------------------------------------+
-| qmlformat | X | X | X | X |
-+----------------------------------------------------------+
-| qmllint | X | X | X | X |
-+----------------------------------------------------------+
-| qmlpreview | X | X | X | X |
-+----------------------------------------------------------+
-| qmlprofiler | X | X | X | X |
-+----------------------------------------------------------+
-| qmlscene | X | X | X | X |
-+----------------------------------------------------------+
-| qmltestrunner | X | X | X | X |
-+----------------------------------------------------------+
-| qmltime | X | X | X | X |
-+----------------------------------------------------------+
-| qmlviewer | X | X | X | X |
-+----------------------------------------------------------+
-| qtdiag | X | X | X | X |
-+----------------------------------------------------------+
-| qtpaths | X | X | X | X |
-+----------------------------------------------------------+
-| qtplugininfo | X | X | X | X |
-+----------------------------------------------------------+
-| qvkgen | X | X | X | X |
-+----------------------------------------------------------+
-| rcc | X | X | X | X |
-+----------------------------------------------------------+
-| tracegen | X | X | X | X |
-+----------------------------------------------------------+
-| uic | X | X | X | X |
-+----------------------------------------------------------+
-| windeployqt | X | X | X | X |
-+----------------------------------------------------------+
-| Target toolchains | | | X | X |
-+----------------------------------------------------------+
-| Qt Debugging Bridge Host Tools | | | X | X |
-+----------------------------------------------------------+
-| qtconfig-gui | | | X | X |
-+----------------------------------------------------------+
-| Qt Emulator | | | X | X |
-+----------------------------------------------------------+
-| Qt Creator VxWorks plugin | | | X | X |
-+----------------------------------------------------------+
-| Qt Creator plugin for Qt | | | | X |
-| Application Manager | | | | |
-+----------------------------------------------------------+
-| qmlinterfacegenerator | | | | X |
-+----------------------------------------------------------+
-| qmltocpp | | | | X |
-+----------------------------------------------------------+
-| qulfontcompiler | | | | X |
-+----------------------------------------------------------+
-| Qt Deployment Server | | | | X |
-+----------------------------------------------------------+
-
-
-Rights for Application and Device use cases
-
-Following table summarizes the rights afforded by different products of the
-Licensed Software to create and distribute Applications and Devices as defined
-in this Agreement (X marks for rights):
-
-+---------------------------------------------------------------+
-| | Applications | Devices |
-+---------------------------------------------------------------+
-| ADP | X | |
-+---------------------------------------------------------------+
-| ADE | X | |
-+---------------------------------------------------------------+
-| DCP | X | X |
-+---------------------------------------------------------------+
-| DCE | X | X |
-+---------------------------------------------------------------+
-
-Licensed Software: Designer tools and modules
-
-The modules and/or tools that are included in the respective product - Qt for
-Design Studio Professional (DSP), Qt for Design Studio Enterprise (DSE) - are
-marked with "X" in the below table.
-
-Designer tools provides no Redistributables.
-
-+---------------------------------------------+
-| | DSP | DSE |
-+---------------------------------------------+
-| Qt Design Studio | X | X |
-+---------------------------------------------+
-| Qt Design Bridges | | X |
-+---------------------------------------------+
-| QML Live on host | X | X |
-+---------------------------------------------+
-| QML Live on target | | X |
-+---------------------------------------------+
-| Variant Management | | X |
-+---------------------------------------------+
-| Shader creation tools | | X |
-+---------------------------------------------+
-| Profiling tools | | X |
-+---------------------------------------------+
-| Simulink support | | X |
-+---------------------------------------------+
-
-
-Both DSP and DSE can be used to create an user interface for use cases covered
-by ADP, ADE, DCP and DCE.
-
-Licensed Software: QA Tools
-
-The modules and/or tools that are included in the respective QA Tools product
-- Squish (both Tester and execution Licenses), Coco or Test Center - are marked
-with "X" in the below table. Optional features that will need additional
-licenses are marked with "O". QA Tools include no Redistributables.
-
-+---------------------------------------------------------------------+
-| | Squish | Coco | Test Center |
-+---------------------------------------------------------------------+
-| Squish IDE | X | | |
-+---------------------------------------------------------------------+
-| QA Tool-specific command line tools | X | X | X |
-+---------------------------------------------------------------------+
-| Coverage Browser | | X | |
-+---------------------------------------------------------------------+
-| HTML interface | | | X |
-+---------------------------------------------------------------------+
-| Qt Support Module | X | | |
-+---------------------------------------------------------------------+
-| Java support module | X | | |
-+---------------------------------------------------------------------+
-| Windows support module | X | | |
-+---------------------------------------------------------------------+
-| iOS support module | X | | |
-+---------------------------------------------------------------------+
-| Android support module | X | | |
-+---------------------------------------------------------------------+
-| Web support module | X | | |
-+---------------------------------------------------------------------+
-| macOS support module | X | | |
-+---------------------------------------------------------------------+
-| VNC support module | X | | |
-+---------------------------------------------------------------------+
-| MCU support module | X | | |
-+---------------------------------------------------------------------+
-| C and C++ language module | | X | |
-+---------------------------------------------------------------------+
-| C# language module | | X | |
-+---------------------------------------------------------------------+
-| QML language module | | X | |
-+---------------------------------------------------------------------+
-| Tester Cross-Compilation Add-On | O | O | |
-+---------------------------------------------------------------------+
-
-License capabilities for Squish
-
-License capabilities that are included in the Squish Tester and Execution
-Licenses are marked with "X" in the below table.
-
-+-----------------------------------------------------------------------------+
-| | Squish Tester License | Squish Execution License |
-+-----------------------------------------------------------------------------+
-| Ability to create, edit, | X | |
-| and debug test cas | | |
-+-----------------------------------------------------------------------------+
-| Ability to execute test | X | X |
-| cases | | |
-+-----------------------------------------------------------------------------+
-
-Install and use capabilities for QA Tools
-
-Install and use capabilities that are included in the respective QA Tools
-products are defined in the below table.
-
-+-----------------------------------------------------------------------------+
-| | Squish | Squish | Coco | Test |
-| | Tester | Execution | License | Center |
-| | License | License | | License |
-+-----------------------------------------------------------------------------+
-| Number of installation | Unlimited | Unlimited | Unlimited | One(1) |
-| instances per license | | | | |
-+-----------------------------------------------------------------------------+
-| Number of concurrent | Limited by| Limited by | Limited by | Limited by |
-| users | number of | number of | number of | number of |
-| | Squish | Squish | Coco | Test Center |
-| | Tester | Execution | Tester | Licenses |
-| | Licenses | Licenses | Licenses | |
-+-----------------------------------------------------------------------------+
-
-
-APPENDIX 2: PRICING
-
-Separate template
-
-APPENDIX 3: ADD-ON PRODUCTS TO LICENSED SOFTWARE
-
-Intentionally left blank.
-
-APPENDIX 4: SMALL BUSINESS AND STARTUP
-
-The provisions of this Appendix 4 are applicable for companies with an annual
-revenue, including funding, equivalent to maximum of 250,000 USD (in applicable
-currency) during the latest full calendar year, as evidenced by duly audited
-records of the Licensee and approved by The Qt Company ("Start-up Company").
-
-Start-up Companies are qualified for a discounted License Fee for maximum of
-four (4) Development Licenses ("Start-up Development License") unless otherwise
-agreed between the parties.
-
-Start-up Development License entitles the respective Designated User for
-Support only for Install Support as defined in Appendix 9, Support Terms.
-
-Upon expiry of the respective Development License Term, the Start-up
-Development Licenses shall be automatically extended, pursuant to Section 3.1
-of the Agreement, for a Renewal Term either as new Start-up Development
-Licenses (if the Licensee still qualifies as a Start-up Company), or as normal
-then standard list price Development Licenses (if the Licensee no longer
-qualifies as a Start-up Company).
-
-APPENDIX 5: NON-COMMERCIAL AND EDUCATIONAL USE
-
-The provisions of this Appendix 5 are applicable for non-commercial use of the
-Licensed Software by the Licensee.
-
-For the purpose of this Appendix 5, the following additional definitions
-(replacing the relevant definition of the Agreement, where applicable) shall be
-applicable:
-
-"Demo Units" shall mean (i) hardware development platform, which incorporates
-the Licensed Software along with Licensee's software and/or hardware, and (ii)
-prototype versions of Applications or Devices.
-
-"Designated User(s)" shall mean the employees and students of the Licensee.
-
-"Licensee Products" shall mean Applications and/or Devices.
-
-"Permitted Purpose" shall mean (i) Licensee's internal evaluation and testing
-of Licensed Software, (ii) building Demo Units as well as (iii) educational
-use.
-
-"Agreement Term" shall mean a period of twelve (12) months or any such other
-period as may be agreed between the Parties.
-
-For the purpose of this Appendix 5, the following changes shall be agreed with
-respect to relevant Sections of the Agreement:
- I. Recital (A) shall be replaced in its entirety to read as follows:
- "(A) Licensee wishes to use the Licensed Software for the Permitted
- Purpose."
- II. Section 3.1 shall be replaced in its entirety to read as follows:
- "The Qt Company grants to Licensee a personal, non-exclusive,
- non-transferable, revocable, royalty-free license, valid for the
- Agreement Term, to use, modify and copy the Licensed Software solely
- for the Permitted Purpose. Licensee may install copies of the Licensed
- Software on five (5) computers per Designated User, provided that only
- the Designated Users who have a valid Development License may use the
- Licensed Software. Licensee may demonstrate the Demo Units, provided
- that such demonstrations must be conducted by Licensee, and the Demo
- Units must remain in Licensee's possession and under Licensee's control
- at all times.
- For clarity, this Agreement does not (i) entitle Licensee to use
- Licensed Software to create Applications or Devices (other than
- prototypes thereof) or (ii) carry any distribution rights to Licensee,
- but such rights are subject to and conditional upon conclusion of a
- separate license agreement with The Qt Company."
- III. Sections 3.2, 3.3, 3.5, 3.6, 8 and 10 shall be deleted.
- IV. Section 3.4 shall be replaced in its entirety to read as follows:
- "Licensee shall not:
- - remove or alter any copyright, trademark or other proprietary rights
- notice contained in any portion of the Licensed Software;
- - transfer, publish, sublicense, disclose, display or otherwise make
- the Licensed Software available to any third party (except that
- Licensee may demonstrate the Demo Units pursuant to Section 3.1);
- - in any way combine, incorporate or integrate Licensed Software with,
- or use Licensed Software for creation of, any software created with
- or incorporating Open Source Qt; Licensee shall cause all Designated
- Users who make use of the licenses granted under this Agreement, to
- be contractually bound to comply with the relevant terms of this
- Agreement and not to use the Licensed Software beyond the terms
- hereof. Licensee shall be responsible for any and all actions and
- omissions of its Designated Users relating to the Licensed Software
- and use thereof. Any use of Licensed Software beyond the provisions
- of this Agreement is strictly prohibited and requires an additional
- license from The Qt Company."
- V. Section 12 shall be replaced in its entirety to read as follows:
- "This Agreement shall enter into force upon due acceptance by both
- Parties and remain in force for the Agreement Term, unless and until
- terminated pursuant to the terms of Section 12.
- Upon termination of the Agreement, Licensee shall cease using the
- Licensed Software. All other copies of Licensed Software in the
- possession or control of Licensee must be erased or destroyed. An
- officer of Licensee must, upon request, promptly deliver to The Qt
- Company a written confirmation that this has occurred."
-
-Except for the modifications specified above, this Appendix carries no change
-to the terms of the Agreement which shall remain in full force.
-
-APPENDIX 6: LICENSE REPORTING
-
-Separate template
-
-APPENDIX 7: MARKETING RIGHTS
-
-This Appendix 7 has the purpose to grant visibility through The Qt Company
-marketing channels of the usage of Qt and related product and service in
-Licensee product. Following related marketing right are agreed between the Qt
-Company and the Licensee.
-
-1. LICENSEE NAME AND LICENSEE LOGO
-
-The Qt Company has the right to use Licensee name and Licensee logo in public
-channel, in respect of the value proposition that the Qt company provided to
-the Licensee.
-
-2. MARKETING CONTENT COOPERATION
-
-2.1. LICENSEE CASES
-
-The Licensee is open to collaborate on content creation for marketing and
-communication purpose. The Licensee will nominate one responsible that will be
-in charge to support The Qt company with this content creation, according to
-content format paragraph, answering technical questions or sharing professional
-picture or video of required content. The Qt Company will have the right to
-advertise this in Content Format and Channel as mentioned in paragraph 3 and 4.
-
-2.2. FINAL PRODUCT REFERRAL
-
-Licensee agree that The Qt Company could connect their software product and
-services with the Licensee device or application, that the Licensee has created
-using The Qt Company technology and competence. Licensee will provide high
-quality picture, and video of the created final product where the Qt technology
-is running into. The Qt Company will have the right to advertise this in
-Content Format and Channel as mentioned in paragraph 3 and 4.
-
-3. CONTENT FORMAT
-
-- Video
-- Written Licensee case
-- Press release
-- Social media posts
-- Emails
-- Event booth Graphics
-- Printed material
-
-4. CHANNELS
-
-- Social media
-- The Qt Company resource center and website
-- Email to the Qt company contact database
-- Events
-- Online webinars
-- Public speech
-- Public presentations
-
-APPENDIX 8: INTENTIONALLY LEFT BLANK
-
-APPENDIX 9: SUPPORT TERMS
-Version 2023-04
-
-These Qt support terms and conditions (“Support Terms”) set forth the legal
-framework, where under The Qt Company (“The Qt Company”) provides support
-services (as herein defined) to the Licensee.
-
-1 DEFINITIONS
-
-“Application Code” shall mean a computer software program written strictly
-using the Qt programming language, by or for the Licensee, with a user
-interface, enabling the Licensee or their users to accomplish a specific task
-and display any results of the task on the display monitor or screen.
-
-“Customer Portal” shall mean The Qt Company’s web-based service and support
-user interface located at https://account.qt.io/ or at another location
-designated by The Qt Company. Customer Portal is used by a Designated User with
-Qt Account, and it provides downloads, license management, license certificate
-and other services for Designated Users.
-
-“Dedicated Contact” shall mean the employee of The Qt Company who will be the
-first point of contact for all Designated Users’ requests for Support.
-
-“Errors” shall mean an error, flaw, mistake, failure, or fault in Licensed
-Software that prevents it from behaving as described in the relevant
-documentation or as agreed between the Parties. Designated User can follow the
-state and progress of Errors in Customer Portal.
-
-“Extended Support” shall mean a continuation to the normal Support period,
-which allows Designated Users to receive selected Support (Standard Support or
-Premium Support) for a version of Licensed Software that is no longer generally
-supported by The Qt Company.
-
-”Install Support” shall mean Support that is limited to installation-related
-Error(s) on Development Platforms specified as supported host platforms for
-each Qt release under doc.qt.io. Install Support covers also operational use of
-the QA Tools, but not operational use of Qt Software.
-
-“Maintenance Release” shall mean a release or version of Licensed Software
-containing bug fixes, error corrections and other changes targeted to
-maintaining and improving product stability and quality. Maintenance Releases
-are generally depicted as a change to the third digit of Licensed Software
-version number.
-
-“Platforms” shall mean both Development Platforms and Deployment Platforms.
-Supported host and target Platforms may vary from for each Qt release as
-defined under doc.qt.io.
-
-“Premium Support” shall mean an upgraded level of Support that The Qt Company
-provides pursuant to these Support Terms to Licensee if Licensee has purchased
-Premium Support instead of Standard Support. Premium Support also covers what
-is included in Standard Support. Premium Support shall always be purchased for
-all Designated User(s) in the respective development team of the Licensee.
-
-“Qt Account” shall mean the Qt Account for a Designated User used for using Qt
-services and Customer Portal. A Qt Account is mapped to the Licensee company
-with the corporate email domain or domains.
-
-”Qualification Kit” shall mean a set of documents and validation test cases
-used for product certification needs as defined in section 2.6.
-
-“Response Time” shall mean the period of time from when Licensee notifies The
-Qt Company about an Error or requests Support until The Qt Company provides
-Licensee with a response that addresses (but not necessarily resolves) the
-reported Error or provides the requested Support.
-
-“Standard Support” shall mean standard level of Support that The Qt Company
-provides pursuant to these Support Terms to Licensee. Standard Support also
-covers what is included in Install Support.
-
-“Security Issue” shall mean an Error that may cause a vulnerability in a system
-or application that uses the Licensed Software.
-
-“Support” shall mean developer assistance that is provided by The Qt Company to
-assist eligible Designated Users in Licensed Software installation, usage and
-functionality problem resolution for Error(s) and Error workarounds pursuant to
-the terms of these Support Terms. Support for different products is available
-as specified in the below table (‘X’ marking the Support that is included in
-the license price, optional Add-on Support services are marked as ‘O’):
-
-+-----------------------------------------------------------------------------+
-| | Install| Standard| Premium| Extended| Qualification|
-| | Support| Support | Support| Support | Kit |
-+-----------------------------------------------------------------------------+
-| DSP | X | X | O | O | |
-+-----------------------------------------------------------------------------+
-| DSE | X | X | O | O | |
-+-----------------------------------------------------------------------------+
-| ADP | X | | | | |
-+-----------------------------------------------------------------------------+
-| ADE | X | X | O | O | |
-+-----------------------------------------------------------------------------+
-| DCP | X | X | O | O | |
-+-----------------------------------------------------------------------------+
-| DCE | X | X | O | O | |
-+-----------------------------------------------------------------------------+
-| Squish | X | X | O | | O |
-+-----------------------------------------------------------------------------+
-| Coco | X | X | O | | O |
-+-----------------------------------------------------------------------------+
-| Test Center | X | X | O | | O |
-+-----------------------------------------------------------------------------+
-| Axivion Suite | X | X | | | |
-+-----------------------------------------------------------------------------+
-| Architecture Analysis | X | X | | | |
-+-----------------------------------------------------------------------------+
-| Static Code Analysis | X | X | | | |
-+-----------------------------------------------------------------------------+
-| Static Coverage | X | X | | | |
-| Analysis Professional | | | | | |
-+-----------------------------------------------------------------------------+
-| Qt Insight | | X | | | |
-+-----------------------------------------------------------------------------+
-
-“Support Validity Term” shall mean the Development License Term or any other
-fixed time period agreed between the Parties during which time the Licensee is
-eligible to receive Support from The Qt Company.
-
-2 SUPPORT SERVICES
-
-2.1 Support Services Provided by The Qt Company
-
-Subject to these Support Terms and during the Support Validity Term, The Qt
-Company will via its Customer Portal, provide Designated User(s) with Support
-for the Licensed Software which Licensee has licensed under the Agreement. The
-Qt Company will make commercially reasonable efforts to solve any Errors
-reported by Designated User(s). Resolution of an Error may be provided through
-Designated User(s) themselves downloading of a later released version of the
-applicable Licensed Software product(s) or providing the Designated User with a
-workaround addressing such Error or providing the Designated User with an
-updated tool configuration.
-
-2.2 Licensee's Obligations
-
-To report an Error, the Designated User shall register the Error on the
-Customer Portal. If the Designated User considers the reported Error to be a
-Security Issue, the Error shall be marked as a Security Issue.
-
-The Designated User must provide adequate information and documentation to The
-Qt Company to enable it to recreate the Error or problem for which the
-Designated User has sought assistance. To ensure efficient handling of Errors,
-the Designated User must provide the following information, where relevant:
-- A clear, detailed description of the problem, question or suggestion;
-- Identification of which Licensed Software product and version is affected;
-- Identification of the operating environment (e.g. operating system, hardware
- Platform, build tools, tool configuration, etc.) on which the problem exists;
-- Marking the issue as a Security Issue, when reporting a Security Issue;
-- On Standard Support: A complete and compilable test case of not more than 500
- lines of code that demonstrates the problem;
-- On Premium Support: A complete and compilable test case that demonstrates the
- problem or access to Application Code source codes.
-
-Additional relevant content, such as screenshots, etc.
-Additional content should be included as attachments. The preferred image
-formats are JPEG and PNG. Compressed content should be included in zip or
-tar.gz archives. Executable content and documents in platform specific formats
-such as Microsoft Office' are not accepted.
-
-In order for The Qt Company to provide prompt handling of Errors, the
-Designated User shall promptly respond to any requests from The Qt Company for
-additional information.
-
-2.3 Support Limitations
-
-General limitations:
-
-Each version or release of the Licensed Software will be Supported under
-Standard Support or Premium Support only for limited time period as set forth
-in doc.qt.io or in documentation provided with the respective Licensed Software
-product. If nothing is documented, a release of Licensed Software is supported
-for one (1) year from the release date of the version x.y.0 and Long Term
-Support (LTS) Releases are supported for a period of three (3) years from the
-release date of the LTS version x.y.0.
-
-The Qt Company shall only provide Support for Designated User(s) through
-Customer Portal.Support is made available for the entire development teams
-only: It is not allowed to purchase Support only for some members of the
-development team, and all Designated Users of the respective development team
-must be eligible for the same level of Support.
-
-Support is not provided for snapshots, preview releases, beta releases or
-release candidates.
-
-The Qt Company shall have no obligation to provide Support for 3rd party
-components, hardware or operating system specific problems or problems arising
-from improper use, accident, neglect, or modification of Qt.
-
-Limitations with Install Support:
-
-Support limited to (i) Error(s) regarding installation and setting up of the Qt
-development environment on host Platforms, or (ii) Errors impacting operational
-use of the QA Tools.
-
-Limitations with Standard Support:
-
-The Qt Company shall not provide Support for third-party software or problems
-caused by third-party software even if such third-party software is distributed
-together with Licensed Software product(s).
-
-The Qt Company shall only provide Support for Error(s) that are reported on and
-can be reproduced on Platforms that are officially supported for the release of
-the Licensed Software.
-
-Limitations with Premium support:
-
-The Qt Company shall not provide Support for third-party software or problems
-caused by third-party software. However, if such third-party software is
-distributed together with Licensed Software, The Qt Company will make
-commercially reasonable efforts to solve such problems.
-
-The Qt Company shall only provide Support for Error(s) that can be reproduced
-on Platforms that are officially supported for the release of the Licensed
-Software. If the Error is on a Platform that is not supported, The Qt Company
-will make commercially reasonable efforts to provide a solution on closest
-corresponding supported Platform.
-
-Premium Support is optional and purchased for an agreed bucket of hours
-(“Bucket”). Hours can be used by any Designated User in the respective
-development team. To encourage continuous usage of the Support, ten percent
-(10%) of the purchased Bucket shall automatically expire (regardless of whether
-such support hours are actually used or not by the Licensee) each month after
-three (3) months from the purchase of the Premium Support.
-
-2.4 Handling of Security Issues
-
-The reported Errors marked as Security Issues will be assessed by experts to
-determine the severity of the issue and to verify if it indeed is a valid
-Security Issue. The Designated User who reported the issue may be contacted for
-more details. If the reported issue is not deemed to be a Security Issue, it
-will be treated as a normal Error and handled accordingly.
-
-A verified Security Issue will be fixed as soon as possible. Qt Company will
-notify all Licensees via appropriate channels about the Security Errors and
-availability of patches for Licensed Software. Typically, a fix for the
-Security Issue is included in the next Maintenance Release of Licensed
-Software.
-
-If the Security Issue is reported in a third-party library used in Licensed
-Software, The Qt Company will notify the relevant third party of such Security
-Issue detected in their library. When the Security Issue is fixed in the
-third-party library, the new version of the third-party library will be in the
-next feasible Maintenance Release of the Licensed Software. If a fixed version
-of the third-party library is not available, The Qt Company may instead decide
-to include documentation regarding the issue, or a patch for this third-party
-library.
-
-All known Security Issues in Licensed Software will be mentioned as part of the
-change notes released with each version of Licensed Software.
-
-2.5 Extended Support
-
-Extended Support extends the Support Validity Term for a release of Licensed
-Software that is no longer generally supported.
-
-Extended Support includes and is by default provided with Standard Support
-rules and limitations, unless Extended Support is purchased together with
-Premium Support in which case Premium Support rules and limitations will apply.
-
-Extended Support is optional and purchased with annual fee and separately per
-each Licensee product. Extended Support will need definition of (i) Licensee
-product, (ii) used Platform(s) and (iii) Licensed Software version(s). For
-avoidance of doubt, Extended Support requires that the Designated User has a
-valid license for the respective Licensed Software.
-
-2.6 Qualification Kit
-
-The Qt Company shall provide a set of documents and validation tests that
-enable the Licensee to qualify QA testing tool (subject to a separate fee) or
-Qt Safe Renderer for the purpose of safety certification of Licensee end-to-end
-solution. Exact complied safety standards may vary between products, used
-features, use case, and industry.
-
-3 RESPONSE TIME
-
-In performing Support, The Qt Company shall commit to following, non-binding,
-Response Times:
-
-Standard Support: Errors and Support requests will have a Response Time not to
-exceed two (2) business days.
-
-Premium Support: Errors and Support requests will have a Response Time not to
-exceed one (1) business day.
-
-Security Issues: Errors that are Security Issues will have a Response Time not
-to exceed one (1) business day.
-
-For complex issues, The Qt Company may provide an initial response to the
-Designated User and then follow up, without undue delay, with additional
-communication before an Error is properly addressed or Support provided.
-
-4 ADDITIONAL SERVICES IN PREMIUM SUPPORT
-
-The Designated User(s) will be assigned a Dedicated Contact to handle requests
-for Support. Dedicated Contact is subject to change in cases such as sick
-leave, vacation and other similar reasons.
-
-The Designated User(s) can on request ask The Qt Company to access their
-computer remotely in order to resolve problems directly.
-
-The Designated User(s) can request a session via Instant Messaging or phone
-call in the support request to The Qt Company.
-
-Premium Support can assist Licensee in implementing new features, bug fixes
-and accessing patches in Licensed Software or Application Code.
-
-All Support requests will be handled with high priority.
-
-5 MAINTENANCE RELEASES, UPDATES AND UPGRADES
-
-Under the Support the Licensee is eligible for Maintenance Releases and Updates
-that The Qt Company generally makes available to customers who has purchased
-Support. Unless otherwise decided by The Company at its free and absolute
-discretion, Upgrades will not be provided under the Support.
-
-The primary focus of Maintenance Releases is product quality. Therefore, each
-Maintenance Release typically includes the following types of changes to the
-previous version of Licensed Software:
-- Bug fixes caused by changes to previously working code;
-- Fixes related to build issues on supported Platforms;
-- Error corrections specific to a single Platform that are not present on other
- Platforms;
-- Corrections to Security Issues;
-- Critical Error corrections such as crashes, data corruption, loss of data,
- race conditions; and
-- Updates to documentation and license information when deemed necessary by
- The Qt Company.
-
-The primary focus of Updates is introducing new features to Licensed Software
-and covering new platforms. Therefore, each Updates typically includes the
-following types of changes to the previous version of Licensed Software:
-- New platform support;
-- New toolchain support;
-- New features and Qt modules;
-
-6 WARRANTY DISCLAIMER
-
-The Qt Company makes no warranties that the Support provided will be successful
-in resolving any difficulties or problems or in diagnosing faults reported by
-Licensee. Support is provided to Licensee on an "as is" basis. To the maximum
-extent permitted by applicable law, The Qt Company disclaims all warranties and
-conditions, either express or implied, including, but not limited to, implied
-warranties of merchantability and fitness for a particular purpose for the
-Support provided by The Qt Company to Licensee.
-
-APPENDIX 10: CONVERSION TO SUBSCRIPTION
-
-Subject to the terms of this Appendix Licensee's current development licenses
-("Current Licenses") for commercial version of Qt Software and the license
-agreements governing such Current Licenses ("Existing Agreements") are being
-replaced by this Agreement and subscription based Development Licenses
-governed hereunder, as further specified below.
-
-+---------------------------------------------------------------------------+
-| Existing Agreement(s) | <Trolltech, Nokia, Digia, The Qt Company> and |
-| signing parties, version | <Licensee> <Version of the Agreement, e.g. 2,0,|
-| and date of signatures | 3.2 or 4.1> <Date of the agreement signatures> |
-| thereof | |
-+---------------------------------------------------------------------------+
-
-Parties hereby agree on conversion of Current Licenses listed in attached
-Exhibit A to the subscription licenses listed in attached Exhibit B for use
-through License Term. As of the date hereof,
-
-i. Licensee's Current Licenses as listed in Exhibit A shall terminate and be
-replaced with the Subscription licenses listed in Exhibit B and;
-ii. Existing Agreements are terminated.
-
-Prices for the conversion of Current Licenses are defined in Appendix 2
-Pricing or Quote.
-
-Notwithstanding anything in this Appendix to the contrary, and in addition to
-any payments due pursuant to this Appendix, Licensee remains fully obligated to
-fulfill any and all outstanding payment obligations to The Qt Company under any
-applicable Existing Agreements. For the avoidance of doubt, if any payments
-remain outstanding on the Current Licenses under the applicable terms Licensee
-will continue to make such payments in accordance with the applicable order
-documentation, notwithstanding the fact that the Current Licenses are being
-converted to Development Licenses pursuant to this Appendix.
-
-APPENDIX 11: TERMS OF USE - QT INSIGHT TRACKER LIBRARY
-Version 1.0
-
-Qt Insight Tracker Library ("Tracker Library") is a software module used to
-collect end user data from Customer's Application and Devices relating to The
-Qt Company's Qt Insight online service the Customer is ordering from The Qt
-Company under a separate service agreement ("Service Agreement").
-
-Unless otherwise set forth herein, definitions written in capital letters used
-herein shall have the meaning set forth in the Service Agreement.
-
-Subject to these terms The Qt Company grants to Customer a worldwide,
-non-exclusive, non-transferable, royalty-free, revocable (for cause) right and
-license, valid for the term of the Service Agreement, to
- (i) use, copy and modify Tracker Library for the purpose of including it
- into the Devices and Applications and solely for the purpose of being
- used only in conjunction with Insight Cloud or Insight Private Cloud,
- and
- (ii) distribute, by itself or through its Contractors, Tracker Library as
- installed, incorporated, or integrated into Applications and/or
- Devices.
-
-Use of Tracker Library in a way or for the purpose other than the above is
-strictly prohibited. Tracker Library is licensed to the Customer in all
-respects "as is".
-
-TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF
-ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER
-WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
-NON-INFRINGEMENT WITH REGARD TO THE TRACKER LIBRARY. THE QT COMPANY DOES NOT
-WARRANT THAT THE TRACKER LIBRARY WILL SATISFY CUSTOMER'S REQUIREMENTS OR THAT
-IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE
-UNINTERRUPTED.
-IN NO EVENT SHALL THE QT COMPANY BE LIABLE TO THE CUSTOMER FOR ANY LOSS OF
-PROFIT, LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT,
-SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY
-KIND, HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THE USE OF THE TRACKER
-LIBRARY.
-
-THE TOTAL MAXIMUM LIABILITY OF THE QT COMPANY UNDER THESE TERMS SHALL IN NO
-EVENT EXCEED 10,000 EUROS.
+Qt Frame Agreement
+Version 2024-02
+
+1. PARTIES OF THIS AGREEMENT
+1.1. This Qt Frame Agreement—comprised of these general terms together with the appendices attached hereto, (hereinafter “Agreement”) is made by and between: The Qt Company, as defined below (hereinafter ”The Qt Company”) AND Customer name (hereinafter “Customer"):___________________ Business Id (e.g. VAT or EIN number):___________________
+1.2. The parties above are hereinafter individually referred to as a "Party" and collectively as the "Parties".
+
+2. STRUCTURE AND OBJECT OF THE AGREEMENT
+2.1. The Parties have entered into this Agreement to agree on the terms and conditions applicable to The Qt Company's delivery of products and services ("Services") to Customer.
+2.2. This Agreement is comprised of the following components:
+(i) This Agreement, which contains the general terms applicable to all Services,
+(ii) Appendices for each of the Services, containing terms applicable to that individual set of Services ("Service Terms"),
+(iii) a Qt Appendix for Pricing, if applicable, which contains pricing for specific Services, and
+(iv) other topic-specific appendices, such as Support or Marketing Rights.
+2.3. Any and all Services purchased shall be specified in, and agreed upon between, the Parties under a separate purchase order, statement of work, quote, or similar document ("Purchase Document"). Each Purchase Document concluded under this Agreement shall include a reference to this Agreement and be governed by this Agreement.
+
+3. DEFINITIONS
+3.1. "Affiliate" of a Party shall mean an entity (i) which is directly or indirectly controlling such Party; (ii) which is under the same direct or indirect ownership or control as such Party; or (iii) which is directly or indirectly owned or controlled by such Party. For these purposes, an entity shall be treated as being controlled by another if that other entity has fifty percent (50 %) or more of the votes in such entity, is able to direct its affairs and/or to control the composition of its board of directors or equivalent body.
+3.2. "Contractor" shall mean third-party consultants, distributors and contractors performing services to Customer under an applicable contractual arrangement.
+3.3. "Customer" shall mean the individual or legal entity specified in Section 1 above, that is a Party to this Agreement.
+3.4. "Force Majeure Event" shall have the meaning set forth in Section 11.7.
+3.5. "Licensed Software" shall mean The Qt Company's commercial software product which is licensed for use by Customer under this Agreement and corresponding Service Terms. Licensed Software shall include, if and to the extent applicable and specified in the applicable relevant Service Terms, corresponding online or electronic documentation, associated media and printed materials, including the source code, and example programs. The Qt Company may in the course of its development activities, at its free and absolute discretion and without any obligation to send or publish any notifications to Customer or in general, make changes, additions or deletions in the components and functionalities of the Licensed Software, provided that no such changes, additions or deletions will affect the already released version of the Licensed Software, but only upcoming version(s). Licensed Software is commercial computer software, developed at private expense and offered to the public under standard commercial terms.
+3.6. "Professional Services" shall mean The Qt Company's professional-, consulting-, training- and/or project services delivered to Customer under this Agreement and specified in a Purchase Document.
+3.7. "Support" shall mean maintenance and support services provided by The Qt Company to assist Customer in using the Licensed Software, as further specified in the Appendix for Support Terms.
+3.8. "The Qt Company" shall mean:
+(i) in the event Customer is an entity residing in the United States or a legal entity incorporated in or having its headquarters in the United States, The Qt Company Inc., a Delaware corporation with its office at 3031 Tisch Way, 110 Plaza West, San Jose, CA 95128, USA.; or
+(ii) in the event Customer is an entity residing outside of the United States or a legal entity incorporated or having its registered office outside of the United States, The Qt Company Oy., a Finnish company with its registered office at Miestentie 7, 02150 Espoo, Finland.
+
+4. PRICES AND PAYMENT
+4.1. The Qt Company agrees to make Services available to Customer subject to the prices set forth in the Appendix for Pricing. In the event that the Appendix for Pricing does not include a price for certain Services, the applicable price shall be the price agreed by the Parties in the respective Purchase Document.
+4.2. All prices are exclusive of value added tax or other taxes, levels, or duties. Value added tax as well as other possible public charges imposed by authorities shall be added to the prices.
+4.3. All fees under this Agreement are non-cancellable and non-refundable.
+4.4. All fees under this Agreement shall be paid by Customer no later than thirty (30) days from the date of the applicable invoice from The Qt Company.
+4.5. Unless otherwise agreed or provided in the respective Service Terms or Purchase Document, The Qt Company will invoice fees for:
+4.5.1. Licensed Software and Support in advance upon conclusion of the Purchase Document, and
+4.5.2. Professional Services monthly in arrears after the Service has been performed.
+4.6. A late payment charge of the lower of: (a) one percent (1%) per month; or (b) the highest interest rate stipulated by applicable law, shall be charged on any unpaid balances that remain past due and which have not been disputed by Customer in good faith within thirty (30) days of receipt of invoice from The Qt Company.
+4.7. The Qt Company may either (i) invoice Customer based on existing agreement, (ii) request Customer to place a purchase order corresponding to a quote by The Qt Company, or (iii) use Customer's stored Credit Card information to automatically charge the Customer for the relevant Renewal Term.
+4.8. Unless and to the extent otherwise agreed in the Appendix for Pricing or in the Purchase Document, The Qt Company shall be entitled to adjust the prices set forth in the Appendix for Pricing by notifying Customer of the change in writing at least sixty (60) days before the effective date of the change. The change shall not affect the current pricing term of Services agreed upon before the effective date of the change.
+
+5. CONFIDENTIALITY
+5.1. The Parties shall keep confidential, and shall not use or disclose to any unauthorized third parties, the existence and content of this Agreement as well as any Confidential Information received from the other Party or otherwise learned in connection with the Agreement or the performance of the Services, without the prior written consent of the other Party. Confidential Information shall mean information that is designated as confidential or that would be reasonably understood to be confidential given the circumstances of disclosure and the nature of the information. The Parties shall not use Confidential Information received from the other Party for any other purposes than the performance of the Agreement or the fulfilment of their rights and obligations hereunder.
+5.2. Each Party shall limit access the other Party's Confidential Information only to those of its employees, subcontractors, Contractors, Affiliates or financial or legal advisors who necessarily need access to the Confidential Information for the proper performance of the Party's rights and obligations under the Agreement. Each Party shall ensure that the persons receiving Confidential Information of the other Party are bound by confidentiality obligations not less restrictive than those stipulated herein.
+5.3. Each Party shall protect the confidentiality of the other Party's Confidential Information with at least the same degree of security as it exercises to its own confidential information, but no less than a standard of reasonable care.
+5.4. The confidentiality obligation stipulated herein shall not be applied to material and information which:
+(iii) has become generally available or otherwise public prior to its submission by the other Party;
+(iv) becomes generally available or otherwise public due to a reason other than the negligence or omission of the recipient or its personnel or other actions in violation of this Agreement or applicable legislation;
+(v) the Party has lawfully received from a third party without any obligation of confidentiality;
+(vi) was lawfully in the possession of the receiving Party prior to receipt of the same from the other Party without any obligation of confidentiality related thereto;
+(vii) a Party has developed independently without using material or information received from the other Party; or
+(viii) a Party must disclose pursuant to law, decree or other order issued by competent regulatory or governmental body or other public authority or a judicial order, in which case the Party shall, to the extent permitted by applicable law, inform the other Party in writing of the disclosure of information prior to such disclosure.
+5.5. Each Party shall, upon request of the other Party at any time, including upon termination, cancellation or expiry of the Agreement, promptly destroy or deliver to the other Party any and all the documents, files, copies and material containing Confidential Information of the other Party. Notwithstanding the foregoing, a Party may retain one copy of the Confidential Information in a secure location, if and solely to the extent required to comply with applicable laws or regulations. Any Confidential Information stored in electronic back-up form shall be rendered inaccessible and destroyed in accordance with standard back-up procedures.
+
+6. INTELLECTUAL PROPERTY RIGHTS
+6.1. Unless and to the extent expressly provided in the respective Service Terms, this Agreement carries no assignment or license to the intellectual property rights of either Party and all such rights are and shall remain the exclusive property of the Party to whom such rights are vested under applicable law at the signing of this Agreement or thereafter.
+6.2. Where The Qt Company's delivery includes any materials owned by a third party, such third party materials shall be governed in all respects by the applicable license terms of such third-party right holders. The Qt Company shall duly inform the Customer whenever such third party materials are included in the Services and of applicable license terms to be followed by the Customer in using such third party materials.
+
+7. FEES AND ORDERING
+7.1. Services Fees. Services Fees are described in the Purchase Document.
+7.2. Ordering Services.
+(i) Customer may purchase Services pursuant to agreed pricing terms or, if no specific pricing terms have been agreed upon, at The Qt Company's standard pricing terms applicable at the time of purchase.
+(ii) Unless expressly otherwise agreed, any price or other term quoted to Customer shall only be valid for the thirty (30) days from the date such price has been quoted.
+
+8. LIMITED WARRANTY AND WARRANTY DISCLAIMER
+8.1. The Qt Company hereby represents and warrants that: (i) it has the power and authority to grant the rights and licenses granted to Customer under this Agreement; (ii) the Licensed Software will operate materially in accordance with its specifications (as set forth in the applicable product documentation or, where relevant, program description); (iii) Professional Services and Support will be performed in a professional, workmanlike manner pursuant to the Agreement; and (iv) during the ten years prior to the effective date of this Agreement, there have not been any claims alleging that the Licensed Software has infringed any intellectual property rights of a third party and, to the knowledge of The Qt Company as of the effective date of this Agreement, no such infringement exists. These warranties do not apply to issues arising from, or relating to, any third-party materials or Customer's use of the Licensed Software in violation of applicable law or the terms of this Agreement.
+8.2. Except to the extent set forth above, the Services are delivered to Customer "as is" and to the maximum extent permitted by applicable law, exclusive of other warranties, whether express, implied, or otherwise. Customer's sole and exclusive remedy and The Qt Company's entire liability for deficiencies or errors in the Services shall be limited, at The Qt Company's option, to correction of the error, replacement of the Services, re-performance of the Service or return of the applicable fees paid for the defective Service for the time period during which Customer was not able to utilize the Service as agreed.
+
+9. LIMITATION OF LIABILITY
+9.1. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) A BREACH OR VIOLATION OF THE OTHER PARTY'S INTELLECTUAL PROPERTY RIGHTS, OR (III) WHERE REQUIRED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOST PROFITS, LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT.
+9.2. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) A BREACH OR VIOLATION OF THE OTHER PARTY'S INTELLECTUAL PROPERTY RIGHTS, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT EXCEED THE AGGREGATE FEES PAID OR PAYABLE TO THE QT COMPANY BY CUSTOMER FOR THE RESPECTIVE LICENSED SOFTWARE OR SERVICE GIVING RISE TO THE LIABILITY. THE FOREGOING LIMITATION WILL NOT APPLY TO CUSTOMER'S OBLIGATION TO PAY THE APPLICABLE FEES CORRESPONDING TO ITS ACTUAL USE OF LICENSED SOFTWARE OR SERVICES.
+
+10. TERM AND TERMINATION
+10.1. This Agreement shall enter into force upon signing by both Parties and is effective as of the last date of signature.
+10.2. This Agreement shall remain in force until further notice and may be terminated without cause by either Party by no less than three (3) months' prior written notice to the other Party.
+10.3. Termination of a particular Purchase Document and the Services governed thereunder shall be stipulated under the applicable Service Terms.
+10.4. Either Party may terminate this Agreement with immediate effect, if the other Party:
+(i) commits a material breach of the terms of this Agreement (including applicable Service Terms) and has not remedied such breach within a reasonable period of time (which shall be no less than thirty (30) days) of the non-breaching Party's written notice specifying the breach, or
+(ii) becomes bankrupt, insolvent or goes into liquidation or debt restructuring.
+10.5. Termination of this Agreement shall, as such, have no effect on the validity of any Services ordered and agreed prior to the effective date of such termination, and such Services shall continue to remain in force pursuant to applicable Service Terms (including the terms of this Agreement) for the remainder of the duration of the applicable Service validity term.
+
+11. GOVERNING LAW AND DISPUTE RESOLUTION
+11.1. The United Nations Convention on Contracts for the International Sale of Goods will not apply to this Agreement.
+11.2. Where this Agreement is concluded with The Qt Company, Inc., a Delaware corporation, the Parties agree that this Section 10.2 will apply. This Agreement will be governed by, and construed in accordance with the laws of the State of California and any controlling United States federal law. Any dispute, controversy or claim arising out of or relating to this contract, including the formation, interpretation, breach or termination thereof, and whether the claims asserted are arbitrable, will be referred to and finally determined by arbitration in accordance with the JAMS International Arbitration Rules. The tribunal will consist of one arbitrator. The place of arbitration will be San Francisco, California, USA. The language to be used in the arbitral proceedings will be English. Judgment upon the award rendered by the arbitrator(s) may be entered in any court having jurisdiction thereof. This Section 10.2 shall not preclude parties from seeking provisional remedies in aid of arbitration from a court of appropriate jurisdiction. Notwithstanding the foregoing, any action by The Qt Company solely to collect license or other fees hereunder may be brought in any court of competent jurisdiction.
+11.3. Where this Agreement is concluded with The Qt Company, Oy., a Finnish company, the parties agree that this Section 10.3 will apply. This Agreement shall be construed and interpreted in accordance with the laws of Finland, excluding its choice of law provisions. All disputes arising out of or in connection with this Agreement shall be finally settled in accordance with the laws of Finland, excluding its choice of law provisions. All disputes arising out of or in connection with this Agreement shall be finally settled under the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators appointed in accordance with the said Rules. The place of arbitration will be Helsinki, Finland. The language to be used in arbitral proceedings will be English. This Section 10.3 shall not preclude parties from seeking provisional remedies in aid of arbitration from a court of appropriate jurisdiction.
+
+12. MISCELLANOUS
+12.1. No Assignment. Customer shall not be entitled to assign or transfer all or any of its rights, benefits and obligations under this Agreement except in case of sale of relevant business or assets or otherwise with prior written consent of The Qt Company, which shall not be unreasonably withheld or delayed. The Qt Company shall be entitled to freely assign or transfer any of its rights, benefits or obligations under this Agreement.
+12.2. Surviving Sections. Any terms and conditions that by their nature or otherwise reasonably should survive termination of this Agreement shall so be deemed to survive.
+12.3. Entire Agreement. This Agreement, its Appendices and any applicable Purchase Documents constitute the complete agreement between the Parties and supersedes all prior or contemporaneous discussions, representations, contracts (including prior License Agreements and similar prior agreements), and proposals, written or oral, with respect to the subject matters discussed herein.
+12.4. Subcontractors. The Qt Company may utilize subcontractors in the performance of Services under this Agreement, provided that The Qt Company remains responsible for the performance of the Services and compliance with this Agreement, as well as ensuring that subcontractors are required to abide by relevant restrictions (e.g., confidentiality) set forth in this Agreement.
+12.5. Modifications. No modification of this Agreement shall be effective unless contained in a writing executed by an authorized representative of each Party. No standard terms and conditions or provisions of any Customer purchase order or other ordering form that Customer may use in connection with the acquisition of Services will modify or affect this Agreement, the parties agree that any such terms and conditions are void with no legal effect.
+12.6. Affiliate Orders. Customer Affiliates may purchase Services via this Agreement as follows:
+(i) any purchases by Customer Affiliates from The Qt Company or its Affiliates will create a contractual relationship directly between the relevant The Qt Company entity and the respective ordering Customer Affiliate;
+(ii) the entry into a Purchase Document between The Qt Company and Customer Affiliate creates an agreement between The Qt Company and Customer Affiliate and incorporates all terms and conditions of this Agreement as the governing agreement between The Qt Company and Customer Affiliate ("Accession Agreement"): and
+(iii) Customer Affiliate will be deemed "Customer" under the terms of this Agreement and all rights and obligations under such Accession Agreement are vested and borne solely by the ordering Customer Affiliate and the relevant The Qt Company entity as contracting parties under such Accession Agreement.
+12.7. Force Majeure. Neither Party shall be liable to the other for any delay or non-performance of its obligations hereunder in the event and to the extent that such delay or non-performance is due to an event of act of God, terrorist attack or other similar unforeseeable catastrophic event that prevents either Party for fulfilling its obligations under this Agreement and which such Party cannot avoid or circumvent ("Force Majeure Event"). If the Force Majeure Event results in a delay or non-performance of a Party for a period of three (3) months or longer, then either Party shall have the right to terminate the relevant Purchase Document and Services thereunder with immediate effect without any liability (except for the obligations of payment arising prior to the Force Majeure Event) towards the other Party.
+12.8. Notices. Any notice given by one Party to the other shall be deemed properly given and deemed received if specifically acknowledged by the receiving Party in writing or when successfully delivered to the recipient by hand, fax, or special courier during normal business hours on a business day to the addresses specified for each Party in this Agreement. Each communication and document made or delivered by one Party to the other Party pursuant to this Agreement shall be in the English language.
+12.9. Attorney Fees. The prevailing Party in any action to enforce this Agreement shall be entitled to recover its attorney's fees and costs in connection with such action.
+12.10. Privacy and Security. The Parties commit to and comply with their respective applicable obligations under the privacy and security terms set forth in the Privacy and Security Appendix and relevant Appendices attached hereto.
+12.11. Feedback. Customer agrees that, from time to time, The Qt Company, may request feedback from Customer regarding the Services ("Feedback"). Customer may choose to provide Feedback and agrees that The Qt Company may freely use, copy, disclose, and exploit any Feedback. No Feedback will be considered Customer Confidential Information unless explicitly agreed otherwise between the Parties.
+12.12. Export Control. Customer acknowledges that the Services, or portions thereof, may be subject to export control restrictions under the applicable laws of respective countries. Customer shall fully comply with all applicable export license restrictions and requirements, economic sanctions restrictions, as well as with all laws and regulations relating thereto, and shall procure all necessary governmental authorizations, including without limitation, all necessary licenses, approvals, permissions, or consents, where necessary (e.g., for re-exportation of the Redistributables, Applications and/or Devices, each as defined in the relevant Service Terms).
+12.13. Severability. If any provision of this Agreement shall be adjudged by any court of competent jurisdiction to be unenforceable or invalid, that provision shall be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable.
+
+13. APPENDICES
+13.1. The following appendices form an integral part of this Agreement. In case of a discrepancy between this Agreement and any of its Appendices, this Agreement shall prevail. In case of discrepancies between the Purchase Document(s) and this Agreement or applicable Service Terms, the terms of this Agreement or the applicable Service Terms shall prevail, except in cases where an express deliberate deviation from the terms of this Agreement or applicable Service Terms has been concluded pursuant to Section 2.3 hereof, in which case the Purchase Document shall prevail.
+1. Appendix for Qt Development Framework
+2. Appendix for Support Terms https://www.qt.io/terms-conditions/support-terms
+3. Appendix for Privacy and Security Terms https://www.qt.io/terms-conditions/privacy-and-security
+
+Appendix for Qt Development Framework
+Version 2024-02
+
+1. This Appendix for Qt Development Framework is an integral part of the Agreement and specifies the legal terms for the licensing of Licensed Software (as defined below) between The Qt Company and the Customer. Entry into this Appendix governs the use of and supersedes any prior contracts between the Parties (including prior License Agreements and similar prior agreements), with respect to the Licensed Software under this Appendix.
+
+2. DEFINITIONS
+2.1. Capitalized words used in this Appendix shall have the meanings described in the Agreement or as defined below.
+2.2. "Add-on Products" shall mean The Qt Company's specific add-on software products which are not licensed as part of The Qt Company's standard Services offerings, but shall be included into the scope of Licensed Software only if so specifically agreed between the Parties.
+2.3. "Application" means software products created using the Licensed Software, which include the Redistributables, or part thereof.
+2.4. "End Customer" shall mean Customer's customer(s) to whom Customer, directly or indirectly, distributes copies of the Redistributables as integrated or incorporated into Applications or Devices.
+2.5. "Data Protection Legislation" shall mean the General Data Protection Regulation (EU 2016/679) (GDPR) and any national implementing laws, regulations and secondary legislation, as may be amended or updated from time to time, as well as any other data protection laws or regulations applicable in the relevant territory.
+2.6. "Deployment Platforms" shall mean target operating systems and/or hardware specified in the License Certificate, on which the Redistributables can be distributed pursuant to the terms and conditions of this Appendix.
+2.7. "Designated User(s)" shall mean the employee(s) of Customer or Customer's Affiliates acting within the scope of their employment or Customer's Contractors acting within the scope of their services on behalf of Customer.
+2.8. "Development License" shall mean the license needed by the Customer for each Designated User to use Licensed Software under the license grant described in Section 5 of this Appendix. Development Licenses are available per respective Licensed Software products; each product having its designated scope and purpose of use.
+2.9. "Development Platforms" shall mean the host operating system(s) specified in the License Certificate, on which Licensed Software can be used under the Development License.
+2.10. "Devices" shall mean
+(i) hardware devices or products that
+a. are manufactured and/or distributed by the Customer, its Affiliates, Contractors or End Customer, and
+b. incorporate, integrate or link to Applications such that substantial functionality of such unit, when used by an End User, is provided by Application(s) or otherwise depends on the Licensed Software; or
+(ii) Applications designed for the hardware devices specified in item (i).
+Devices covered by this Appendix shall be specified in the Pricing Appendix or Purchase Document.
+2.11. "Distribution License(s)" shall mean a royalty-bearing license required for any kind of sale, trade, exchange, loan, lease, rental or other distribution by or on behalf of Customer to a third party of Redistributables in connection with Devices pursuant to license grant described in Section 5.3 of this Appendix. Distribution Licenses are sold separately for each type of Device respectively and cannot be used for any other type of Devices.
+2.12. "Distribution License Packs" shall mean set of prepaid Distribution Licenses for distribution of Redistributables, as defined in The Qt Company's standard price list, quote, Pricing Appendix or in the Purchase Document, as applicable.
+2.13. "Evaluation License Term" shall mean a time period specified in the License Certificate for the Customer to use the relevant Licensed Software for evaluation purposes according to Section 5.6 of this Appendix.
+2.14. "Intellectual Property Rights" shall mean patents (including utility models), design patents, and designs (whether or not capable of registration), chip topography rights and other like protection, copyrights, trademarks, service marks, trade names, logos or other words or symbols and any other form of statutory protection of any kind and applications for any of the foregoing as well as any trade secrets.
+2.15. "License Certificate" shall mean a certificate generated by The Qt Company for each Designated User respectively upon their download of the Licensed Software, which will be available under the respective Designated User's Qt Account at account.qt.io. License Certificates will specify relevant information pertaining to the Licensed Software purchased by Customer and the license to the Licensed Software.
+2.16. "License Fee" shall mean the fee charged to Customer for rights granted under this Appendix.
+2.17. "Licensed Software" shall mean the specified product(s) of Qt Software which Customer has purchased and which is provided to Customer under the terms of this Appendix (including its Exhibits). Licensed Software shall include corresponding online or electronic documentation, associated media and printed materials, including source code (where applicable), example programs and the documentation. Licensed Software does not include Third Party Software (as defined in Section 6) or Qt Community Edition. The Qt Company may, in the course of its development activities, at its free and absolute discretion and without any obligation to send or publish any notifications to Customer or in general, make changes, additions or deletions in the components and functionalities of the Licensed Software, provided that no such changes, additions or deletions will affect the already released version of the Licensed Software, but only upcoming version(s).
+2.18. "License Term" shall mean the agreed validity period of the Development License during which the relevant Licensed Software product can be used pursuant to this Appendix. The agreed License Term, as ordered and paid for by Customer, shall be memorialized in the applicable License Certificate.
+2.19. "Customer's Records" shall mean books and records that contain information bearing on Customer's compliance with the Agreement, Customer's use of Qt Community Edition and/or the payments due to The Qt Company under the Agreement, including, but not limited to user information, assembly logs, sales records and distribution records.
+2.20. "Modified Software" shall have the meaning as set forth below in Section 4.
+2.21. "Qt Software" shall mean the development and design software of The Qt Company, which The Qt Company makes available under commercial and/or open source licenses as either the "Licensed Software" or the "Qt Community Edition".
+2.22. "Permitted Software" shall mean third party products that are generally available to the public, which may include parts of Qt Community Edition or be developed using Qt Community Edition.
+2.23. "Pre-Release Code" shall have the meaning as set forth in Section 7.
+2.24. "Prohibited Combination" shall mean any effort to use, combine, incorporate, link or integrate Licensed Software with any software created with or incorporating Qt Community Edition, or use Licensed Software for creation of any such software.
+2.25. "Qt Community Edition" shall mean the open source version of Qt Software available under the terms of the GNU Lesser General Public License, version 2.1 or later ("LGPL") or the GNU General Public License, version 2.0 or later ("GPL"). For clarity, Qt Community Edition shall not be provided, governed or used under this Appendix.
+2.26. "Redistributables" shall mean the portions of Licensed Software as set forth in Exhibit 1 hereto that may be distributed pursuant to this Appendix in object code form only, including any relevant documentation. Where relevant, any reference to Licensed Software in this Appendix includes and refers to Redistributables.
+2.27. "Renewal Term" shall mean an extension of the previous License Term as agreed between the Parties.
+2.28. "Submitted Modified Software" shall have the meaning as set forth in Section 4.2 of this Appendix.
+2.29. "Third-Party Software" shall have the meaning set forth in Section 6 of this Appendix.
+2.30. "Updates" shall mean a release or version of the Licensed Software containing bug fixes, error corrections and other changes that are generally made available to users of the Licensed Software that have contracted for Support. Updates are generally depicted as a change to the digits following the decimal in the Licensed Software version number. The Qt Company shall make Updates available to Customer under the Support. Updates shall be considered as part of the Licensed Software hereunder.
+2.31. "Upgrades" shall mean a release or version of the Licensed Software containing enhancements and new features and are generally depicted as a change to the first digit of the Licensed Software version number. In the event that Upgrades are provided to Customer under this Appendix, they shall be considered as part of the Licensed Software hereunder.
+
+3. OWNERSHIP
+3.1. Ownership of The Qt Company
+3.1.1. The Licensed Software is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The Licensed Software is licensed, not sold.
+3.1.2. All of The Qt Company's Intellectual Property Rights are and shall remain the exclusive property of The Qt Company or its respective licensors . No rights to The Qt Company's Intellectual Property Rights are assigned or granted to Customer under this Appendix, except when and to the extent expressly specified herein.
+3.2. Ownership of Customer
+3.2.1. All of Customer's Intellectual Property Rights are and shall remain the exclusive property of Customer or its licensors respectively.
+3.2.2. Except to the extent set forth in this Appendix, all Intellectual Property Rights to the Modified Software, Applications and Devices (except to Redistributables included therein) shall remain with Customer.
+
+4. MODIFIED SOFTWARE
+4.1. Customer may create bug-fixes, error corrections, patches or modifications to the Licensed Software ("Modified Software"). To the extent that Customer's Modified Software breaks source or binary compatibility or other functionality with the Licensed Software, Customer acknowledges that The Qt Company's ability to provide Support may be prevented or limited and Customer's ability to make use of Updates may be restricted.
+4.2. Customer may, at its sole and absolute discretion, choose to submit Modified Software to The Qt Company ("Submitted Modified Software") in connection with Customer's Support request, service request or otherwise. In the event Customer does so, then, Customer hereby grants The Qt Company a sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and fully paid-up license, under all of Customer's Intellectual Property Rights, to reproduce, adapt, translate, modify, and prepare derivative works of, publicly display, publicly perform, sublicense, make available and distribute such Submitted Modified Software as The Qt Company sees fit at its free and absolute discretion.
+
+5. LICENSES GRANTED
+5.1. Development with Licensed Software
+5.1.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable license, valid for each License Term, to use, modify and copy the Licensed Software by Designated Users on the Development Platforms for the sole purposes of designing, developing, demonstrating and testing Application(s) and/or Devices, and to provide support and other services related to such Applications and Devices to End Customers. Each Application and/or Device can only include, incorporate or integrate contributions by such Designated Users who are duly licensed for the applicable Development Platform(s) and Deployment Platform(s) (i.e have a valid license for the appropriate Licensed Software product and only use one type of Qt Development License per Customer Application and/or Device(s)).
+5.1.2. Customer may install copies of the Licensed Software on five (5) computers per Designated User, provided that only Designated Users who have a valid Development License may use the Licensed Software.
+5.1.3. Customer may designate another Designated User to replace a then-current Designated User by notifying The Qt Company in writing, where such replacement is due to termination of employment, long-term absence or other permanent reason affecting Designated User's need for Licensed Software.
+5.1.4. Upon expiry of the initially agreed License Term, the respective License Term shall be automatically extended by one or more Renewal Term(s), unless and until either Party notifies the other Party in writing, that it does not wish to continue the License Term, such notification to be provided to the other Party no less than thirty (30) days before expiry of the respective License Term. The Qt Company shall, in good time before the due date for the above notification, remind the Customer on the coming Renewal Term. Unless otherwise agreed between the Parties, Renewal Term shall be equal to the length of the previous License Term, but no longer than thirty-six (36) months.
+5.1.5. Any such Renewal Term shall be subject to License Fees agreed between the Parties or, if no advance agreement exists, subject to The Qt Company's standard list pricing applicable at the commencement date of any such Renewal Term.
+5.2. Distribution of Applications
+5.2.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable, perpetual, royalty-free and revocable (only for Customer’s material breach of agreement) right and license to:
+(i) distribute, by itself or through its Contractors, Redistributables as installed, incorporated or integrated into Applications for execution on the Deployment Platforms; and
+(ii) grant perpetual and irrevocable sublicenses to Redistributables, as distributed hereunder, for End Customers solely to the extent necessary in order for the End Customers to use the Applications for their respective intended purposes.
+5.2.2. Right to distribute the Redistributables as part of an Application as provided herein is not royalty-bearing but is conditional upon the Application having been created, updated and maintained under a valid and duly paid Development License.
+5.3. Distribution of Devices
+5.3.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable, perpetual, revocable (only for Customer’s material breach of agreement), royalty-bearing right and license to:
+(i) distribute, by itself or through one or more tiers of Contractors, Redistributables as installed, incorporated or integrated, or intended to be installed, incorporated or integrated into Devices for execution on the Deployment Platforms; and
+(ii) grant perpetual and irrevocable sublicenses to Redistributables, as distributed hereunder, for End Customers solely to the extent necessary in order for the End Customers to use the Devices for their respective intended purposes.
+5.3.2. Right to distribute the Devices as provided herein is conditional upon (i) the Devices having been created, updated and maintained under a valid and duly paid Development License, and (ii) Customer having acquired corresponding Distribution Licenses at the time of distribution of any Devices to End Customers.
+5.4. Further Requirements
+5.4.1. The licenses granted in this Section 5 by The Qt Company to Customer are conditional and subject to Customer's compliance with the following terms:
+(i) Customer acknowledges that The Qt Company has separate products for the purpose of Applications and Devices respectively, where development and distribution of Devices is only allowed using the correct designated product. Customer shall ensure and bear the burden of proof that Customer is using a correct product entitling Customer to development and distribution of Devices;
+(ii) Customer shall not remove or alter any copyright, trademark or other proprietary rights notice(s) contained in any portion of the Licensed Software;
+(iii) Applications must add primary and substantial functionality to Licensed Software so as not to compete with the Licensed Software;
+(iv) Applications may not pass on functionality which in any way makes it possible for others to create software with Licensed Software; provided however that Customer may use Licensed Software’s scripting and QML ("Qt Quick") functionality solely in order to enable scripting, themes and styles that augment the functionality and appearance of the Application(s) without adding primary and substantial functionality to the Application(s);
+(v) Customer shall not use Licensed Software in any manner or for any purpose that infringes, misappropriates or otherwise violates any Intellectual Property Right or right of any third party, or that violates any applicable law;
+(vi) Customer shall not use The Qt Company's or any of its suppliers' names, logos, or trademarks to market Applications, except that Customer may use “Built with Qt” logo to indicate that an Application or Device was developed using Licensed Software;
+(vii) Customer shall not distribute, sublicense or disclose source code of Licensed Software to any third party (provided however that Customer may appoint employee(s) of Contractors and Affiliates as Designated Users to use Licensed Software pursuant to this Appendix).
+(viii) Customer shall not grant the End Customers a right to: (a) make copies of the Redistributables except when and to the extent required to use the Applications and/or Devices for their intended purpose; (b) modify the Redistributables or create derivative works thereof; (c) decompile, disassemble or otherwise reverse engineer Redistributables; or (d) redistribute any copy or portion of the Redistributables to any third party, except as part of the onward sale of the Application or Device on which the Redistributables are installed;
+(ix) Customer shall not, and shall cause that its Affiliates or Contractors shall not, use Licensed Software in any Prohibited Combination, unless Customer has received specific advance written permission from The Qt Company to do so. Absent such written permission, any and all distribution by Customer during the term of the Agreement of a hardware device or product: a) which incorporates or integrates any part of Licensed Software or Qt Community Edition; or b) where substantial functionality is provided by software built with Licensed Software or Qt Community Edition or otherwise depends on Licensed Software or Qt Community Edition, shall be considered to be Device distribution under this Appendix and shall be dependent on Customer’s compliance thereof (including but not limited to the obligation to pay applicable License Fees for such distribution). Notwithstanding the foregoing, Customer is entitled to use and combine Licensed Software with Permitted Software;
+(x) Customer shall cause all of its Affiliates, Contractors and End Customer entitled to make use of the licenses granted under this Appendix, to be contractually bound to comply with the relevant terms hereof and not to use the Licensed Software beyond the terms hereof nor for any purposes other than operating within the scope of their services for Customer. Customer shall be responsible for any and all actions and omissions of its Affiliates, Contractors, and End Customers relating to the Licensed Software and use thereof (including but not limited to payment of all applicable License Fees);
+(xi) Except when and to the extent explicitly provided in this Section 5, Customer shall not transfer, publish, disclose, display or otherwise make available the Licensed Software; and
+(xii) Customer shall not attempt or enlist a third party to conduct or attempt to conduct any of the above.
+5.4.2. The above terms shall not be applicable if and solely to the extent they conflict with any mandatory provisions of applicable laws.
+5.4.3. Any use of Licensed Software beyond the provisions of this Appendix is strictly prohibited and requires, at a minimum an additional license from The Qt Company (e.g. certain additional rights granted under software development kit “SDK” agreement with regard to limitations of Section 5.4.1 iv, vii or viii).
+5.5. Evaluation License
+5.5.1. Subject to the terms of this Appendix, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable license, valid for the Evaluation License Term to use the relevant Licensed Software product solely for Customer’s internal use to evaluate and determine whether the Licensed Software meets Customer's business requirements, specifically excluding any commercial use of the Licensed Software or any derived work thereof.
+5.5.2. Upon the expiry of the Evaluation License Term, Customer must either discontinue use of the relevant Licensed Software or acquire a commercial Development License specified herein.
+
+6. THIRD-PARTY SOFTWARE. The Licensed Software may provide links or access to third party libraries or code (collectively "Third-Party Software") to implement various functions. Third-Party Software does not, however, comprise part of the Licensed Software, but is provided to Customer complimentary and use thereof is discretionary for Customer. Third-Party Software will be listed in the ".../src/3rdparty" source tree delivered with the Licensed Software or documented in the Licensed Software, as such may be amended from time to time. Customer acknowledges that use or distribution of Third-Party Software is in all respects subject to applicable license terms of applicable third-party right holders.
+
+7. PRE-RELEASE CODE
+7.1. The Licensed Software may contain pre-release code and functionality, or sample code marked or otherwise stated with appropriate designation such as "Technology Preview", "Alpha", "Beta", "Experimental", "Sample", "Example" etc. ("Pre-Release Code").
+7.2. Such Pre-Release Code may be provided complimentary for Customer, in order to provide experimental support or information for new platforms or preliminary versions of one or more new functionalities, or for other similar reasons. Pre-Release Code may not be at the level of performance and compatibility of a final, generally available, product offering. Pre-Release Code may not operate correctly, may contain errors and may be substantially modified by The Qt Company prior to a commercial product release, if any. The Qt Company is under no obligation to make Pre-Release Code commercially available, or provide any Support or Updates relating thereto. To the maximum extent permitted by law, the Qt Company assumes no liability whatsoever regarding any Pre-Release Code and any use thereof is exclusively at Customer's own risk and expense.
+7.3. Unless Licensed Software specifies different license terms for the respective Pre-Release Code, Customer is entitled to use such pre-release code pursuant to Section 5 of this Appendix, just like other Licensed Software.
+
+8. SUPPORT. Support is provided according to agreed support level and subject to applicable requirements and restrictions, as specified in the Appendix for Support Terms.
+
+9. FEES AND ORDERING: DISTRIBUTION LICENSES
+9.1. Distribution License Packs
+9.1.1. Unless otherwise agreed in writing, Distribution Licenses shall be purchased by way of Distribution License Packs.
+9.1.2. Upon due payment of the ordered Distribution License Pack(s), Customer will have an account of Distribution Licenses available for distributing the Redistributables in accordance with this Agreement.
+9.2. Each time Customer distributes a copy of Redistributables, one Distribution License is used and Customer's account of available Distribution Licenses is decreased accordingly.
+9.3. Customer may distribute copies of the Redistributables so long as Customer has Distribution Licenses remaining on its account.
+
+10. RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS
+10.1. Customer's Record-keeping
+10.1.1. Customer shall at all times during the term of the Agreement or validity of any of the licenses hereunder, whichever is later, and for a period of two (2) years thereafter, maintain Customer's Records in an accurate and up-to-date form. Customer's Records shall be adequate to reasonably enable The Qt Company to determine Customer's compliance with the provisions of the Agreement. The records shall conform to general good accounting practices.
+10.1.2. Customer shall, within thirty (30) days from receiving The Qt Company's request to that effect, deliver to The Qt Company a report based on Customer's Records, such report to contain information, in sufficient detail, on: (i) number and identity of users working with Licensed Software or Qt Community Edition, (ii) copies of Redistributables distributed by Customer during the most recent calendar quarter and/or any other term specified by The Qt Company, and (iii) any other information pertaining to Customer's compliance with the terms of the Agreement (e.g. information on products and/or projects relating to use of Distribution Licenses), as The Qt Company may reasonably require from time to time.
+10.2. The Qt Company's Audit Rights
+10.2.1. The Qt Company or an independent auditor acting on behalf of The Qt Company may, upon at least thirty (30) days' prior written notice and at The Qt Company expense, audit Customer with respect to Customer's use of the Licensed Software, but not more frequently than once during each six (6) month period. Such audit may be conducted by mail, electronic means or through an in-person visit to Customer's place of business. Any possible in-person audit shall be conducted during regular business hours at Customer's facilities, shall not unreasonably interfere with Customer's business activities and shall be limited in scope to verify Customer's compliance with the terms of the Agreement. The Qt Company or its independent auditor shall be entitled to inspect Customer's Records and conduct necessary interviews of Customer's relevant employees and Contractors. All Customer's Records and use thereof shall be subject to the obligation of confidentiality under the Agreement.
+10.2.2. If an audit reveals that Customer is using the Licensed Software beyond scope of the licenses Customer has paid for, Customer shall pay to The Qt Company any amounts owed for such unauthorized use within thirty (30) days from receipt of the corresponding invoice from The Qt Company.
+10.2.3. In addition, in the event the audit reveals a material violation of the terms of the Agreement (without limitation, either (i) underpayment of more than 10% of License Fees or 10,000 euros (whichever is more) or (ii) distribution of products, which include or result from Prohibited Combination, shall be deemed a material violation for purposes of this section), then Customer shall pay The Qt Company's reasonable cost of conducting such audit.
+
+11. TERMINATION
+11.1. Termination of Licenses
+11.1.1. The Qt Company may terminate Customer's rights to any and all Licensed Software (including access to Support), if Customer:
+(i) commits a material breach of the Agreement (including this Appendix) and has not remedied the breach within a reasonable period of time (which shall be no less than 30 days) of The Qt Company's written notice specifying the breach, or
+(ii) becomes bankrupt, insolvent or goes into liquidation or debt restructuring.
+11.2. Suspension of rights: Instead of termination, The Qt Company reserves the right to suspend or withhold grants of any and all rights to the Licensed Software (including Support), should Customer fail to make payment in timely fashion or otherwise violate or is reasonably suspected of violating its obligations under the Agreement and/or this Appendix, and where such violation or breach is not cured within ten (10) business days following The Qt Company's written notice thereof.
+11.3. Parties Rights and Duties upon Termination
+11.3.1. Upon expiry or termination of the Development Licenses, Customer shall cease and shall cause all Designated Users (including those of its Affiliates' and Contractors') to cease using the relevant Licensed Software.
+11.3.2. Upon such expiry or termination of Development Licenses, Customer shall destroy or return to The Qt Company all copies of the respective Licensed Software and all related materials and will certify the same by Customer's duly authorized officer to The Qt Company upon its request, provided however that Customer may retain and utilize such copies of the Licensed Software to the extent required to provide Customer's continued support to End Customers, for archiving purposes or as is required under applicable law.
+11.3.3. Distribution Licenses are perpetual and, therefore, Customer's distribution rights hereunder shall only terminate upon The Qt Company's termination of Distribution Licenses due to Customer's material breach as set forth in Section 11.1.1(i) of this Appendix. In case of such termination by The Qt Company due to Customer's material breach, Customer must cease any distribution of Applications and Devices at the effective date of termination.
+11.3.4. Expiry or termination of any of Customer's licenses hereunder for any reason whatsoever shall not:
+(i) relieve Customer of its obligation to pay any License Fees accrued or payable to The Qt Company prior to the effective date of termination, and Customer pay to The Qt Company all such fees within 30 days from the effective date of termination of the licenses;
+(ii) relieve Customer of its obligation to ensure that Applications and Devices (including those already distributed) remain in compliance with the terms of the Agreement; nor
+(iii) affect any rights of End Customer to continue use of Applications and Devices (and therein incorporated Redistributables).
+11.4. Extension of Rights under Special Circumstances. In the event that, during the applicable License Term, The Qt Company is declared bankrupt under a final, non-cancellable decision by relevant court of law, and the Agreement is not, at the date of expiry of the Development License(s), assigned to a party who has assumed The Qt Company's position as a legitimate licensor of Licensed Software under the Agreement, then all valid Development Licenses possessed by Customer at such date of expiry, and which Customer has not notified for expiry, shall be extended to be valid in perpetuity under the terms of the Agreement. Any such extension shall not apply to The Qt Company's Support obligations.
+
+EXHIBIT 1, Licensed Software
+At the time of conclusion of this Appendix, the latest available version of Licensed Software includes the software libraries and tools set forth in Exhibit 1 (as provided below), depending on which product(s) Customer has purchased under the relevant Purchase Document.
+The modules and tools are specific to each product version respectively and may vary from version to version. Modules and tools included in the latest publicly available version of the respective product at any given time are listed in Exhibit 1 of https://www.qt.io/terms-conditions/qt-dev-framework/exhibit-1. If a new version of Licensed Software does not include a module or tool present in an older version which Customer is entitled to use under a valid license from The Qt Company, then Customer will continue to have such right during the validity of Customer's license to relevant Licensed Software. In the event a new version of the Licensed Software adds modules or tools to any previous version(s), Customer's rights will extend to cover also such additional modules and tools.
+
+EXHIBIT 2 - Small Business Terms
+1. This Exhibit applies to entities that qualify as a Qualified Small Business (defined below) and provides additional terms and conditions applicable to small business pricing and licensing. In the event that Customer is a Qualified Small Business and there is any conflict between the terms of this Exhibit and any other terms of the Agreement, the terms in this Exhibit shall take precedence.
+
+2. APPLICABILITY FOR SMALL BUSINESS LICENSES. Any small business discounts applied require that Customer (including any Customer Affiliates or group entities) has an annual revenue (including annual capital funding) below 1 Million EUR, or the equivalent thereof, as approved by The Qt Company (each, a "Qualified Small Business"). The annual revenue, including funding, must be evidenced upon request by business records and approved by The Qt Company in its reasonable discretion.
+
+3. SUPPORT. Support is limited to: (i) Install Support; and (ii) for any other Standard Support issue, five (5) support tickets annually.
+
+4. LIMITATION ON NUMBER OF SMALL BUSINESS DEVELOPER LICENSES. Qualified Small Business discounts and purchasing structure may be applied to a maximum of three discounted developer licenses (either ADE or DCP) per Qualified Small Business. Any additional licenses purchased will be at The Qt Company list price in effect at the time.
+
+5. LIMITATION FOR NUMBER OF INSTALLATIONS. Customer may install copies of the Licensed Software on two (2) computers per Designated User, provided that only the Designated Users who have a valid Development License may use the Licensed Software.
+
+6. CONDITIONAL WAIVER OF DISTRIBUTION LICENSES. For Qualified Small Businesses, the Agreement requirements to purchase Distribution Licenses for Devices shall apply only when Customer ceases to be a Qualified Small Business (e.g., when annual revenue threshholds are bypassed).
+
+7. ADDITIONAL TERMS FOR RENEWALS. The initial subscription purchase term for Qualified Small Business Licenses is twelve (12) months. Upon expiration of the initial twelve (12) month term and unless terminated in accordance with the Agreement, the Licenses will automatically renew for additional twelve (12) month terms with applicable Qualified Small Business discounts. If Customer ceases to be a Qualified Small Business, renewal pricing shall be at The Qt Company list price in effect at the time of renewal, or as agreed in writing between the parties.
+
+8. ADDITIONAL AUDIT RIGHTS. In addition to the audit rights set forth in the Agreement, The Qt Company reserves the right to audit Customer financial records in order to determine whether Customer is a Qualified Small Business.
--- /dev/null
+[flake8]
+ignore = E115,E265,W503
+max-line-length = 100
+exclude = rc_*.py,*_rc.py,ui_*.py
+per-file-ignores =
+ # for init_test_paths() hack
+ *_test_*.py:E402
+ __init__.py:F401,E402
+
"*_test.py"
],
"python.testing.pytestEnabled": false,
- "python.testing.unittestEnabled": true
+ "python.testing.unittestEnabled": true,
+ "flake8.args": [
+ "--config=.flake8"
+ ]
}
or macOS only).
* `--verbose-build`, will output the compiler invocation with command line
arguments, etc.
+ * `--disable-pyi`, will suppress the generation of .pyi files. This allows
+ debugging when the project builds but the pyi generator complains.
## Requirements
* QtBluetooth
* QtCharts
* QtDataVisualization
+* QtGraphs
* QtMultimedia
* QtMultimediaWidgets
* QtNetworkAuth
* QtScxml
* QtSensors
* QtSerialPort
+* QtSerialBus
+* QtSpatialAudio
* QtStateMachine
+* QtTextToSpeech
* QtVirtualKeyboard
* QtWebChannel
* QtWebEngineCore
* QtPdf
* QtPdfWidgets
* QtHttpServer
+* QtLocation
+* QtAsyncio
### Documentation and Bugs
win32
[QtQml::bug_825]
py3.8 # bug in typeobject::type_mro_modified, fix in 3.9
- py3.9 # fixed in 3.9.12
- py3.10 # fixed in 3.10.4
+ py3.9.0
+ py3.9.1
+ py3.9.2
+ py3.9.3
+ py3.9.4
+ py3.9.5
+ py3.9.6
+ py3.9.7
+ py3.9.8
+ py3.9.9
+ py3.9.10
+ py3.9.11
+ # fixed in 3.9.12
+ py3.10.0
+ py3.10.1
+ py3.10.2
+ py3.10.3
+ # fixed in 3.10.4
# PYSIDE-535: These errors are still present. Please try to remove one :)
[sample::mixed_mi]
pypy
# embedding_generator.py. Pass it as a separate option.
cmake_cmd.append(f"-DQFP_PYTHON_HOST_PATH={sys.executable}")
else:
- cmake_cmd.append(f"-DPYTHON_EXECUTABLE={self.py_executable}")
- cmake_cmd.append(f"-DPYTHON_INCLUDE_DIR={self.py_include_dir}")
- cmake_cmd.append(f"-DPYTHON_LIBRARY={self.py_library}")
+ cmake_cmd.append(f"-DPython_EXECUTABLE={self.py_executable}")
+ cmake_cmd.append(f"-DPython_INCLUDE_DIR={self.py_include_dir}")
+ cmake_cmd.append(f"-DPython_LIBRARY={self.py_library}")
# If a custom shiboken cmake config directory path was provided, pass it to CMake.
if OPTION["SHIBOKEN_CONFIG_DIR"] and config.is_internal_pyside_build():
else:
log.warning('numpy include directory was not found.')
- if self.build_type.lower() == 'debug':
- if not self.is_cross_compile:
- cmake_cmd.append(f"-DPYTHON_DEBUG_LIBRARY={self.py_library}")
- else:
+ if self.build_type.lower() != 'debug':
if OPTION['NO_STRIP']:
cmake_cmd.append("-DQFP_NO_STRIP=1")
if OPTION['NO_OVERRIDE_OPTIMIZATION_FLAGS']:
"(default yes if applicable, i.e. Python "
"version >= 3.8 and release build if on Windows)")
+ if OPTION["DISABLE_PYI"]:
+ cmake_cmd.append("-DDISABLE_PYI=yes")
+
if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
else:
cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF")
-
if OPTION['COMPILER_LAUNCHER']:
compiler_launcher = OPTION['COMPILER_LAUNCHER']
cmake_cmd.append(f"-DCMAKE_C_COMPILER_LAUNCHER={compiler_launcher}")
if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE and self.make_generator == "Ninja":
make_doc_cmd.append("-v")
if run_process(make_doc_cmd) != 0:
- raise SetupError("Error generating documentation "
- f"for {extension}")
+ raise SetupError(f"Error generating documentation for {extension}")
else:
log.info("Sphinx not found, skipping documentation build")
else:
# (lib/libclang.lib), whereas we want to copy the shared
# library (bin/libclang.dll).
clang_lib_path = Path(re.sub(r'lib/libclang.lib$',
- 'bin/libclang.dll',
- clang_lib_path))
+ 'bin/libclang.dll',
+ clang_lib_path))
else:
clang_lib_path = Path(clang_lib_path)
# shiboken6 links against libclang.so.6 or a similarly
message = "Patched rpath to '$ORIGIN/' in"
if sys.platform.startswith('linux'):
+
def rpath_cmd(srcpath):
final_rpath = ''
# Command line rpath option takes precedence over
elif sys.platform == 'darwin':
message = "Updated rpath in"
+
def rpath_cmd(srcpath):
final_rpath = ''
# Command line rpath option takes precedence over
if self.name == PYSIDE:
self.sphinx_src = self.out_dir / "rst"
example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
- assert(example_gallery.is_file())
+ assert example_gallery.is_file()
example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
example_gallery_cmd.append("--quiet")
ADDITIONAL_OPTIONS = """
Additional options:
--limited-api Use Limited API [yes/no]
- ---macos-use-libc++ Use libc++ on macOS
+ --macos-use-libc++ Use libc++ on macOS
--snapshot-build Snapshot build
--package-timestamp Package Timestamp
--cmake-toolchain-file Path to CMake toolchain to enable cross-compiling
--shiboken-host-path Path to host shiboken package when cross-compiling
--qt-host-path Path to host Qt installation when cross-compiling
+ --disable-pyi Disable .pyi file generation
"""
"VERBOSE_BUILD": has_option('verbose-build'),
"SNAPSHOT_BUILD": has_option("snapshot-build"),
"LIMITED_API": option_value("limited-api"),
+ "DISABLE_PYI": has_option("disable-pyi"),
"PACKAGE_TIMESTAMP": option_value("package-timestamp"),
# This is used automatically by setuptools.command.install object, to
# specify the final installation location.
# qtpaths is available. This happens when building the host
# tools in the overall cross-building process.
use_cmake = False
- if (using_cmake_toolchain_file or
- (not self.qmake and not self.qtpaths and self.qt_target_path)):
+ if (using_cmake_toolchain_file or (not self.qmake
+ and not self.qtpaths and self.qt_target_path)):
use_cmake = True
QtInfo().setup(qtpaths_abs_path, self.cmake, qmake_abs_path,
# explicitly. This is to help with the building of host tools
# while cross-compiling.
# Skip this process for the 'build_rst_docs' command
- if not self.is_cross_compile and not self.qt_target_path and 'build_rst_docs' not in sys.argv:
+ if not (self.is_cross_compile and not self.qt_target_path
+ and 'build_rst_docs' not in sys.argv):
# Enforce usage of qmake in QtInfo if it was given explicitly.
if self.qmake:
self.has_qmake_option = True
copy_qml = True
copy_translations = True
copy_qt_conf = True
- should_copy_icu_libs = True
log.info("Copying files...")
copy_qml = False
copy_translations = False
copy_qt_conf = False
- should_copy_icu_libs = False
# <qt>/lib/* -> <setup>/{st_package_name}/Qt/lib
destination_dir = Path("{st_build_dir}/{st_package_name}".format(**_vars))
_filter=accepted_modules,
recursive=False, _vars=_vars, force_copy_symlinks=True)
- if should_copy_icu_libs and not cross_build and not is_android:
+ if not cross_build and not is_android:
# Check if ICU libraries were copied over to the destination
# Qt libdir.
maybe_icu_libs = find_files_using_glob(destination_qt_lib_dir, "libicu*")
if sys.platform.startswith("linux"):
scripts.append("android_deploy.py")
+ scripts.append("requirements-android.txt")
script_dirs.extend(["deploy_lib/android",
"deploy_lib/android/recipes/PySide6",
"deploy_lib/android/recipes/shiboken6",])
src = f"{{install_dir}}/bin/{script_dir}"
target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
# Exclude subdirectory tests
- copydir(src, target, _filter=["*.py", "*.spec", "*.jpg"], recursive=False, _vars=_vars)
+ copydir(src, target, _filter=["*.py", "*.spec", "*.jpg", "*.icns", "*.ico"],
+ recursive=False, _vars=_vars)
# <install>/bin/* -> {st_package_name}/
executables.extend(copydir(
src = f"{{install_dir}}/bin/{script_dir}"
target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
# Exclude subdirectory tests
- copydir(src, target, _filter=["*.py", "*.spec"], recursive=False, _vars=_vars)
+ copydir(src, target, _filter=["*.py", "*.spec", "*.jpg", "*.icns", "*.ico"],
+ recursive=False, _vars=_vars)
# <install>/bin/*.exe,*.dll -> {st_package_name}/
filters = ["pyside*.exe", "pyside*.dll"]
module_QtWidgets(),
module_QtHelp(),
module_QtNetwork(),
- module_QtConcurent(),
+ module_QtConcurrent(),
module_QtDBus(),
module_QtDesigner(),
module_QtOpenGL(),
return data
-def module_QtConcurent() -> ModuleData:
+def module_QtConcurrent() -> ModuleData:
data = ModuleData("Concurrent")
return data
product_dependency:
../../qt/qt5:
- ref: "e6ff9950e1d0365d2c4f231fd1b6fe194144fd52"
+ ref: "15b7e7434fd79334a5cf071e36cee7663fe1fb45"
dependency_source: supermodule
dependencies: [
"../../qt/qt3d",
equals_value: Linux
- type: EnvironmentVariable
variableName: interpreter
- variableValue: "python3.8"
+ variableValue: "python3.11"
enable_if:
condition: property
property: host.osVersion
from build_scripts.options import has_option, option_value
from build_scripts.utils import (expand_clang_variables, get_ci_qtpaths_path,
get_qtci_virtualEnv,
- parse_cmake_conf_assignments_by_key, remove_tree,
- run_instruction)
+ parse_cmake_conf_assignments_by_key,
+ remove_tree, run_instruction)
log.setLevel(logging.INFO)
CI_ENV_AGENT_DIR = option_value("agentdir")
CI_COMPILER = option_value("compiler")
CI_USE_SCCACHE = option_value("compiler-launcher")
-CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(calendar.timegm(datetime.datetime.now().timetuple()))
+CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(
+ calendar.timegm(datetime.datetime.now().timetuple())
+)
CI_FEATURES = []
_ci_features = option_value("features")
if _ci_features is not None:
- for f in _ci_features.split(', '):
+ for f in _ci_features.split(", "):
CI_FEATURES.append(f)
CI_RELEASE_CONF = has_option("packaging")
CI_TEST_PHASE = option_value("phase")
-if CI_TEST_PHASE not in ["ALL", "BUILD", "WHEEL"]:
+if CI_TEST_PHASE not in ["ALL", "BUILD"]:
CI_TEST_PHASE = "ALL"
def get_current_script_path():
- """ Returns the absolute path containing this script. """
+ """Returns the absolute path containing this script."""
try:
this_file = __file__
except NameError:
pyside_project_dir = os.path.join(setup_script_dir, "sources", "pyside6")
d = parse_cmake_conf_assignments_by_key(pyside_project_dir)
- release_version_type = d.get('pyside_PRE_RELEASE_VERSION_TYPE')
- pre_release_version = d.get('pyside_PRE_RELEASE_VERSION')
+ release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
+ pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
if pre_release_version and release_version_type:
return True
return False
print("call_setup")
print("python_ver", python_ver)
print("phase", phase)
- _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH)
+ _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(
+ python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH
+ )
if phase in ["BUILD"]:
remove_tree(_env, True)
python3 = "python3"
if sys.platform == "win32":
python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
- run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"], "Failed to pin virtualenv")
+ run_instruction(
+ [python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+ "Failed to pin virtualenv",
+ )
# installing to user base might not be in PATH by default.
env_path = os.path.join(site.USER_BASE, "bin")
v_env = os.path.join(env_path, "virtualenv")
v_env = "virtualenv"
run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
# When the 'python_ver' variable is empty, we are using Python 2
- # Pip is always upgraded when CI template is provisioned, upgrading it in later phase may cause perm issue
- run_instruction([str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies")
+ # Pip is always upgraded when CI template is provisioned,
+ # upgrading it in later phase may cause perm issue
+ run_instruction(
+ [str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
+ )
cmd = [env_python, "-u", "setup.py"]
if phase in ["BUILD"]:
cmd += ["build", "--standalone", "--unity"]
- elif phase in ["WHEEL"] or CI_RELEASE_CONF:
- cmd += ["bdist_wheel", "--reuse-build", "--standalone", "--skip-cmake", "--skip-make-install", "--only-package"]
- cmd += ["--build-tests",
- "--log-level=verbose"]
+ cmd += ["--build-tests", "--log-level=verbose"]
if CI_TARGET_ARCH == "X86_64-ARM64":
cmd += ["--macos-arch='x86_64;arm64'"]
env = os.environ
run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
-if __name__ == "__main__":
+if __name__ == "__main__":
# Remove some environment variables that impact cmake
- arch = '32' if CI_TARGET_ARCH == 'X86' else '64'
+ arch = "32" if CI_TARGET_ARCH == "X86" else "64"
expand_clang_variables(arch)
- for env_var in ['CC', 'CXX']:
+ for env_var in ["CC", "CXX"]:
if os.environ.get(env_var):
del os.environ[env_var]
python_ver = "3"
- if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH !="aarch64":
- python_ver = "3.8"
+ if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH != "aarch64":
+ python_ver = "3.11"
wheel_package_dir = "qfpa-p3.6"
if CI_TARGET_OS in ["Windows"]:
- if (os.environ.get('HOST_OSVERSION_COIN')).startswith('windows_10'):
+ if (os.environ.get("HOST_OSVERSION_COIN")).startswith("windows_10"):
python_ver = "3.10.0"
else:
python_ver = "3.8.1"
if os.environ.get("QTEST_ENVIRONMENT") == "ci" and sys.platform == "win32":
signing_dir = str(os.environ.get("PYSIDE_SIGNING_DIR"))
print("Check for signing dir " + signing_dir)
- assert(os.path.isdir(signing_dir))
- if CI_TEST_PHASE in ["ALL", "WHEEL"] and sys.platform != "win32":
- # "Old" Windows wheels won't be signed anyway so there is no need to
- # create those, so that we don't accidentally release those.
- call_setup(python_ver, "WHEEL")
+ assert os.path.isdir(signing_dir)
import sys
from build_scripts.log import log
-from build_scripts.options import has_option, log, option_value
+from build_scripts.options import has_option, option_value
from build_scripts.utils import (expand_clang_variables, get_ci_qmake_path,
get_qtci_virtualEnv, remove_tree, run_instruction)
def call_testrunner(python_ver, buildnro):
- _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH)
+ _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH,
+ CI_TARGET_ARCH)
remove_tree(_env, True)
# Pinning the virtualenv before creating one
# Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
if CI_HOST_OS == "MacOS" and CI_HOST_ARCH == "ARM64":
v_env = "virtualenv"
run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
- run_instruction([env_pip, "install", "-r", "requirements.txt"], "Failed to install dependencies")
+ run_instruction([env_pip, "install", "-r", "requirements.txt"],
+ "Failed to install dependencies")
else:
- run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"], "Failed to pin virtualenv")
+ run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+ "Failed to pin virtualenv")
# installing to user base might not be in PATH by default.
env_path = os.path.join(site.USER_BASE, "bin")
v_env = os.path.join(env_path, "virtualenv")
v_env = "virtualenv"
run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
# When the 'python_ver' variable is empty, we are using Python 2
- # Pip is always upgraded when CI template is provisioned, upgrading it in later phase may cause perm issue
- run_instruction([env_pip, "install", "-r", "requirements.txt"], "Failed to install dependencies")
+ # Pip is always upgraded when CI template is provisioned,
+ # upgrading it in later phase may cause perm issue
+ run_instruction([env_pip, "install", "-r", "requirements.txt"],
+ "Failed to install dependencies")
# Install distro to replace missing platform.linux_distribution() in python3.8
run_instruction([env_pip, "install", "distro"], "Failed to install distro")
# Try to install built wheels, and build some buildable examples.
if CI_RELEASE_CONF:
wheel_tester_path = os.path.join("testing", "wheel_tester.py")
- # We create wheels differently in Qt CI with Windows and there are no "old" wheels
- if CI_HOST_OS != "Windows":
- # Run the test for the old set of wheels
- cmd = [env_python, wheel_tester_path, qmake_path]
- run_instruction(cmd, "Error while running wheel_tester.py on old wheels")
-
- # Uninstalling the other wheels
- run_instruction([env_pip, "uninstall", "shiboken6", "shiboken6_generator", "pyside6", "-y"],
- "Failed to uninstall old wheels")
-
# Run the test for the new set of wheels
- cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist_new", "--new"]
+ cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
else:
call_testrunner("3.8.1", str(testRun))
elif CI_HOST_OS == "Linux":
- call_testrunner("3.8", str(testRun))
+ call_testrunner("3.11", str(testRun))
else:
call_testrunner("3", str(testRun))
from typing import List, Optional, Tuple
import build # type: ignore
+import pyproject_hooks
+import build_scripts.wheel_files
from build_scripts.wheel_files import (ModuleData, # type: ignore
set_pyside_package_path,
wheel_files_pyside_addons,
PACKAGE_FOR_WHEELS = "package_for_wheels"
+PYSIDE_DESCRIPTION = "Python bindings for the Qt cross-platform application and UI framework"
@dataclass
def generate_pyproject_toml(artifacts: Path, setup: SetupData) -> str:
content = None
+ _name = setup.name
_tag = get_platform_tag()
+ _console_scripts = ""
+ if setup.console_scripts:
+ _formatted_console_scripts = "\n".join(setup.console_scripts)
+ _console_scripts = f"[project.scripts]\n{_formatted_console_scripts}"
+
+ # Installing dependencies
+ _dependencies = []
+ if _name in ("PySide6", "PySide6_Examples"):
+ _dependencies.append(f"shiboken6=={setup.version[0]}")
+ _dependencies.append(f"PySide6_Essentials=={setup.version[0]}")
+ _dependencies.append(f"PySide6_Addons=={setup.version[0]}")
+ elif _name == "PySide6_Essentials":
+ _dependencies.append(f"shiboken6=={setup.version[0]}")
+ elif _name == "PySide6_Addons":
+ _dependencies.append(f"shiboken6=={setup.version[0]}")
+ _dependencies.append(f"PySide6_Essentials=={setup.version[0]}")
+ elif _name == "shiboken6_generator":
+ _dependencies.append(f"shiboken6=={setup.version[0]}")
+
with open(artifacts / "pyproject.toml.base") as f:
content = (
f.read()
.replace("PROJECT_DESCRIPTION", f'"{setup.description}"')
.replace("PROJECT_README", f'"{setup.readme}"')
.replace("PROJECT_TAG", f'"{_tag}"')
+ .replace("PROJECT_SCRIPTS", _console_scripts)
+ .replace("PROJECT_DEPENDENCIES", f"{_dependencies}")
)
-
return content
else:
fext = "Shiboken"
- # Installing dependencies
- install_requires = []
- if name in ("PySide6", "PySide6_Examples"):
- install_requires.append(f"shiboken6=={setup.version[0]}")
- install_requires.append(f"PySide6_Essentials=={setup.version[0]}")
- install_requires.append(f"PySide6_Addons=={setup.version[0]}")
- elif _name == "PySide6_Essentials":
- install_requires.append(f"shiboken6=={setup.version[0]}")
- elif _name == "PySide6_Addons":
- install_requires.append(f"shiboken6=={setup.version[0]}")
- install_requires.append(f"PySide6_Essentials=={setup.version[0]}")
-
# For special wheels based on 'PySide6'
# we force the name to be PySide6 for the package_name,
# so we can take the files from that packaged-directory
content = f.read().format(
name=_name,
fake_ext=fext,
- install=install_requires,
- console_scripts={"console_scripts": setup.console_scripts},
)
return content
description="Python/C++ bindings generator",
readme="README.shiboken6-generator.md",
console_scripts=[
- "shiboken6 = shiboken6_generator.scripts.shiboken_tool:main",
- "shiboken6-genpyi = shiboken6_generator.scripts.shiboken_tool:genpyi",
+ 'shiboken6 = "shiboken6_generator.scripts.shiboken_tool:main"',
+ 'shiboken6-genpyi = "shiboken6_generator.scripts.shiboken_tool:genpyi"',
],
)
# Also, the tool should not exist in any other platform than Linux
_console_scripts = []
if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
- _console_scripts = ["pyside6-android-deploy = PySide6.scripts.pyside_tool:android_deploy"]
+ _console_scripts = ['pyside6-android-deploy = "PySide6.scripts.pyside_tool:android_deploy"']
_pyside_tools.remove("android_deploy")
- _console_scripts.extend([f"pyside6-{tool} = PySide6.scripts.pyside_tool:{tool}"
+ _console_scripts.extend([f'pyside6-{tool} = "PySide6.scripts.pyside_tool:{tool}"'
for tool in _pyside_tools])
setup = SetupData(
name="PySide6_Essentials",
version=get_version_from_package("PySide6", package_path), # we use 'PySide6' here
- description="Python bindings for the Qt cross-platform application and UI framework (Essentials)",
+ description=f"{PYSIDE_DESCRIPTION} (Essentials)",
readme="README.pyside6_essentials.md",
console_scripts=_console_scripts
)
setup = SetupData(
name="PySide6_Addons",
version=get_version_from_package("PySide6", package_path), # we use 'PySide6' here
- description="Python bindings for the Qt cross-platform application and UI framework (Addons)",
+ description=f"{PYSIDE_DESCRIPTION} (Addons)",
readme="README.pyside6_addons.md",
console_scripts=[],
)
setup = SetupData(
name="PySide6",
version=get_version_from_package("PySide6", package_path),
- description="Python bindings for the Qt cross-platform application and UI framework",
+ description=PYSIDE_DESCRIPTION,
readme="README.pyside6.md",
console_scripts=[],
)
raise Exception("Unable to determine build directory, no matching virtual environment found")
+def check_modules_consistency():
+ available_functions = dir(build_scripts.wheel_files)
+ functions = [i.replace("module_", "") for i in available_functions if i.startswith("module_")]
+
+ sources = [i.stem for i in Path("sources/pyside6/PySide6/").glob("Qt*")]
+
+ missing_modules = set(sources) - set(functions)
+
+ if len(missing_modules):
+ print("Warning: the following modules don't have a function "
+ f"in 'build_scripts/wheel_files.py':\n {missing_modules}")
+
+ # Check READMEs
+ readme_modules = set()
+ for r in Path(".").glob("README.pyside6*"):
+ with open(r) as f:
+ for line in f:
+ if line.startswith("* Qt"):
+ readme_modules.add(line.strip().replace("* ", ""))
+
+ missing_modules_readme = set(sources) - readme_modules
+
+ if len(missing_modules_readme):
+ print("Warning: the following modules are not in READMEs :"
+ f"\n {missing_modules_readme}")
+
+
if __name__ == "__main__":
parser = ArgumentParser()
)
options = parser.parse_args()
+ # Sanity check between the available modules,
+ # and the functions in build_scripts/wheel_files.py
+ check_modules_consistency()
+
build_directory = get_build_directory(options)
verbose = False
# 5. call the build module to create the wheel
print("-- Creating wheels")
if not verbose:
- _runner = build.pep517.wrappers.quiet_subprocess_runner
+ _runner = pyproject_hooks.quiet_subprocess_runner
else:
- _runner = build.pep517.wrappers.default_subprocess_runner
+ _runner = pyproject_hooks.default_subprocess_runner
builder = build.ProjectBuilder(package_path, runner=_runner)
- builder.build("wheel", "dist_new")
+ builder.build("wheel", "dist")
- # 7. Copy wheels back
- print("-- Copying wheels to dist_new/")
- dist_path = Path("dist_new")
+ # 6. Copy wheels back
+ print("-- Copying wheels to dist/")
+ dist_path = Path("dist")
if not dist_path.is_dir():
dist_path.mkdir()
- for wheel in Path(package_path / "dist_new").glob("*.whl"):
+ for wheel in Path(package_path / "dist").glob("*.whl"):
copy(wheel, dist_path / wheel.name)
# 8. Remove leftover files
(PySide6_Examples).
- [PYSIDE-748] An optional parameter "tag" has been added to @Slot, allowing
to set QMetaMethod.tag().
- - [PYSIDE-2500] QMetaMethod.invoke() has been added. The invocation functions
- of QMetaObject now accept up to 10 arguments.
+ - [PYSIDE-769] QtAsyncio: Support for ThreadPoolExecutor has been added.
- [PYSIDE-841] QtQuick3D procedural texture and QRhi window examples have
been added.
- [PYSIDE-2230] Support for Python 3.12 has been added.
- [PYSIDE-2485] A QLocale-related crash on macOS has been fixed.
- [PYSIDE-2487] Error messages around signal/slot connections have been improved.
- [PYSIDE-2494] The install location of QtAsyncio for CMake builds has been fixed.
+ - [PYSIDE-2500] QMetaMethod.invoke() has been added. The invocation functions
+ of QMetaObject now accept up to 10 arguments.
- [PYSIDE-2509] The signature of SignalInstance.connect() has been fixed.
- [PYSIDE-2510] An error is now set when instantiating a Signal on a non-QObject.
- [PYSIDE-2514] The type hints of QAbstractItemView.setModel()/QGraphicsView.setScene()
--- /dev/null
+Qt for Python 6.6.2 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-535] PyPySide has been updated to Python 3.10
+ - [PYSIDE-769] QtAsyncio: call_soon_threadsafe() and QtAsyncio.run() (with
+ [PYSIDE-1112] keep_running and quit_qapp arguments) have been implemented.
+ [PYSIDE-2561] A bug with Python 3.12 has been fixed. Documentation has
+ [PYSIDE-2575] been added. Keyboard interrupts are now caught. Cancelling
+ waiting tasks has been fixed. Tasks are now awaitable.
+ - [PYSIDE-1106] Documentation: The generation process has been optimized
+ and warnings have been reduced.
+ - [PYSIDE-1586] Qt Charts: class QColorAxis has been added.
+ - [PYSIDE-1612] Android Deployment: The QtQuick dependency is now added
+ when present.
+ - [PYSIDE-1612] Deployment problems when using pyenv have been fixed.
+ - [PYSIDE-1612] Nuitka has been upgraded to 1.8.0
+ - [PYSIDE-1612] Deployment: An icon for the application has been added.
+ - [PYSIDE-1612] A separate requirements.txt for Android deployment has been
+ added.
+ - [PYSIDE-1612] Android Deployment: `buildozer` is now installed with the
+ --init cli argument
+ - [PYSIDE-1931] Further hangs in QSql(Relational)TableModel.select() (PostGres
+ with SSH tunnel) have been fixed.
+ - [PYSIDE-2206] Examples quick3d/proceduraltexture, sql/books have been
+ updated. The contactslist has been added. Examples supported
+ on Android are tagged as `Android`.
+ - [PYSIDE-2439] FindPython is now used instead of the deprecated
+ FindPythonInterp and FindPythonLibs CMake modules.
+ - [PYSIDE-2539] Documentation: The enum name is now generated for flags.
+ - [PYSIDE-2544] A crash accessing QDBusVariant.variant() has been fixed.
+ - [PYSIDE-2547] A hang in QDBusConnection.connect() has been fixed.
+ - [PYSIDE-2558] Documentation: the differences between commercial and lts
+ releases have been clarified.
+ - [PYSIDE-2568] A crash of pyside6-designer with pyenv on Unix has been
+ fixed.
+ - [PYSIDE-2574] Documentation: A favicon for browser tabs has been added.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-1106] Documentation: An option to disable the inheritance diagram
+ has been added.
+ - [PYSIDE-1735] An error in the generated code when all enum values are
+ deprecated has been fixed.
+ - [PYSIDE-2404] pyi file generation can now be disabled for debugging.
+ - [PYSIDE-2530] yocto cross builds have been fixed to find
+ shiboken_wrapper.sh.
+ - [PYSIDE-2577] Documentation/doxygen: A bug querying const functions
+ has been fixed.
from PySide6.QtGui import (QColor, QFont, QPalette)
from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
-from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+import PySide6.QtAsyncio as QtAsyncio
import asyncio
-import signal
import sys
from random import randint
main_window.show()
- signal.signal(signal.SIGINT, signal.SIG_DFL)
-
- asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
- asyncio.ensure_future(eratosthenes.start())
- asyncio.get_event_loop().run_forever()
+ QtAsyncio.run(eratosthenes.start())
from PySide6.QtCore import (Qt, QObject, Signal, Slot)
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
-from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+import PySide6.QtAsyncio as QtAsyncio
import asyncio
-import signal
import sys
main_window.show()
- signal.signal(signal.SIGINT, signal.SIG_DFL)
-
- asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
- asyncio.get_event_loop().run_forever()
+ QtAsyncio.run()
from PySide6.QtAxContainer import QAxSelect, QAxWidget
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (QApplication, QDialog,
- QMainWindow, QMessageBox, QToolBar)
+ QMainWindow, QMessageBox, QToolBar)
class MainWindow(QMainWindow):
fileMenu.addAction(exitAction)
aboutMenu = self.menuBar().addMenu("&About")
- aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
+ aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt) # noqa: F821
aboutMenu.addAction(aboutQtAct)
self.axWidget = QAxWidget()
self.setCentralWidget(self.axWidget)
item = QListWidgetItem(label)
pairing_status = self._local_device.pairingStatus(info.address())
if (pairing_status == QBluetoothLocalDevice.Paired
- or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
+ or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
item.setForeground(QColor(Qt.green))
else:
item.setForeground(QColor(Qt.black))
items = self._ui.list.findItems(address.toString(), Qt.MatchContains)
color = QColor(Qt.red)
- if pairing == QBluetoothLocalDevice.Paired or pairing == QBluetoothLocalDevice.AuthorizedPaired:
+ if (pairing == QBluetoothLocalDevice.Paired
+ or pairing == QBluetoothLocalDevice.AuthorizedPaired):
color = QColor(Qt.green)
for item in items:
item.setForeground(color)
from PySide6.QtBluetooth import QBluetoothLocalDevice
from PySide6.QtQml import QmlElement
-from PySide6.QtCore import QObject, Property, Signal, Slot, Qt, QCoreApplication
+from PySide6.QtCore import QObject, Property, Signal, Slot, Qt
from heartrate_global import simulator, is_android
if is_android:
permission = QBluetoothPermission()
permission.setCommunicationModes(QBluetoothPermission.Access)
- permission_status = qApp.checkPermission(permission)
+ permission_status = qApp.checkPermission(permission) # noqa: F821
if permission_status == Qt.PermissionStatus.Undetermined:
- qApp.requestPermission(permission, self, self.initLocalDevice)
+ qApp.requestPermission(permission, self, self.initLocalDevice) # noqa: F821
return
if permission_status == Qt.PermissionStatus.Denied:
return
from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
QBluetoothDeviceInfo)
from PySide6.QtQml import QmlElement
-from PySide6.QtCore import QTimer, Property, Signal, Slot, Qt, QCoreApplication
+from PySide6.QtCore import QTimer, Property, Signal, Slot, Qt
from bluetoothbaseclass import BluetoothBaseClass
from deviceinfo import DeviceInfo
if is_android:
permission = QBluetoothPermission()
permission.setCommunicationModes(QBluetoothPermission.Access)
- permission_status = qApp.checkPermission(permission)
+ permission_status = qApp.checkPermission(permission) # noqa: F821
if permission_status == Qt.PermissionStatus.Undetermined:
- qApp.requestPermission(permission, self, self.startSearch)
+ qApp.requestPermission(permission, self, self.startSearch) # noqa: F82 1
return
elif permission_status == Qt.PermissionStatus.Denied:
return
# Disconnect and delete old connection
if self.m_control:
self.m_control.disconnectFromDevice()
- m_control = None
+ self.m_control = None
# Create new controller and connect it if device available
if self.m_currentDevice:
#! [Filter HeartRate service 2]
# If heartRateService found, create new service
if self.m_foundHeartRateService:
- self.m_service = self.m_control.createServiceObject(QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
+ self.m_service = self.m_control.createServiceObject(
+ QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
if self.m_service:
self.m_service.stateChanged.connect(self.serviceStateChanged)
self.info = "Discovering services..."
elif switch == QLowEnergyService.RemoteServiceDiscovered:
self.info = "Service discovered."
- hrChar = self.m_service.characteristic(QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
+ hrChar = self.m_service.characteristic(
+ QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
if hrChar.isValid():
- self.m_notificationDesc = hrChar.descriptor(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
+ self.m_notificationDesc = hrChar.descriptor(
+ QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
if self.m_notificationDesc.isValid():
self.m_service.writeDescriptor(self.m_notificationDesc,
QByteArray.fromHex(b"0100"))
@Slot(QLowEnergyCharacteristic, QByteArray)
def confirmedDescriptorWrite(self, d, value):
if (d.isValid() and d == self.m_notificationDesc
- and value == QByteArray.fromHex(b"0000")):
+ and value == QByteArray.fromHex(b"0000")):
# disabled notifications . assume disconnect intent
self.m_control.disconnectFromDevice()
self.m_service = None
# disable notifications
if (self.m_notificationDesc.isValid() and self.m_service
- and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
+ and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
self.m_service.writeDescriptor(self.m_notificationDesc,
QByteArray.fromHex(b"0000"))
else:
self.m_sum += value
self.m_avg = float(self.m_sum) / len(self.m_measurements)
self.m_calories = ((-55.0969 + (0.6309 * self.m_avg) + (0.1988 * 94)
- + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
+ + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
self.statsChanged.emit()
Bluetooth Low Energy Heart Rate Game
====================================
+.. tags:: Android
+
The Bluetooth Low Energy Heart Rate Game shows how to develop a
Bluetooth Low Energy application using the Qt Bluetooth API. The
application covers the scanning for Bluetooth Low Energy devices,
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import os
-import sys
_simulator = False
global _simulator
_simulator = s
+
is_android = os.environ.get('ANDROID_ARGUMENT')
char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
char_data.setValue(QByteArray(2, 0))
char_data.setProperties(QLowEnergyCharacteristic.Notify)
- client_config = QLowEnergyDescriptorData(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration,
- QByteArray(2, 0))
+ client_config = QLowEnergyDescriptorData(
+ QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration, QByteArray(2, 0))
char_data.addDescriptor(client_config)
service_data = QLowEnergyServiceData()
value = QByteArray()
value.append(chr(0)) # Flags that specify the format of the value.
value.append(chr(current_heart_rate)) # Actual value.
- characteristic = service.characteristic(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
- assert(characteristic.isValid())
+ characteristic = service.characteristic(
+ QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+ assert characteristic.isValid()
# Potentially causes notification.
service.writeCharacteristic(characteristic, value)
if current_heart_rate == 60:
def characteristic(self, characteristic):
self._characteristic = characteristic
self.characteristic_changed.emit()
-
QML_IMPORT_NAME = "Scanner"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
@QmlSingleton
class Device(QObject):
def stop_device_discovery(self):
if self.discovery_agent.isActive():
self.discovery_agent.stop()
-
-
def set_device(self, device):
self._device = device
self.device_changed.emit()
-
Bluetooth Low Energy Scanner Example
====================================
+.. tags:: Android
+
A Python application that demonstrates the analogous example in Qt
`Bluetooth Low Energy Scanner <https://doc.qt.io/qt-6/qtbluetooth-lowenergyscanner-example.html>`_
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
-from device import Device
+from device import Device # noqa: F401
from pathlib import Path
if __name__ == '__main__':
@service.setter
def service(self, service):
self._service = service
-
-
"""PySide6 port of the Callout example from Qt v5.x"""
import sys
-from PySide6.QtWidgets import (QApplication, QGraphicsScene,
- QGraphicsView, QGraphicsSimpleTextItem, QGraphicsItem)
+from PySide6.QtWidgets import (QApplication, QGraphicsScene, QGraphicsView,
+ QGraphicsSimpleTextItem, QGraphicsItem)
from PySide6.QtCore import Qt, QPointF, QRectF, QRect
from PySide6.QtCharts import QChart, QLineSeries, QSplineSeries
from PySide6.QtGui import QPainter, QFont, QFontMetrics, QPainterPath, QColor
# establish the position of the anchor point in relation to _rect
above = anchor.y() <= self._rect.top()
- above_center = (anchor.y() > self._rect.top() and
- anchor.y() <= self._rect.center().y())
- below_center = (anchor.y() > self._rect.center().y() and
- anchor.y() <= self._rect.bottom())
+ above_center = (anchor.y() > self._rect.top()
+ and anchor.y() <= self._rect.center().y())
+ below_center = (anchor.y() > self._rect.center().y()
+ and anchor.y() <= self._rect.bottom())
below = anchor.y() > self._rect.bottom()
on_left = anchor.x() <= self._rect.left()
- left_of_center = (anchor.x() > self._rect.left() and
- anchor.x() <= self._rect.center().x())
- right_of_center = (anchor.x() > self._rect.center().x() and
- anchor.x() <= self._rect.right())
+ left_of_center = (anchor.x() > self._rect.left()
+ and anchor.x() <= self._rect.center().x())
+ right_of_center = (anchor.x() > self._rect.center().x()
+ and anchor.x() <= self._rect.right())
on_right = anchor.x() > self._rect.right()
# get the nearest _rect corner.
x = (on_right + right_of_center) * self._rect.width()
y = (below + below_center) * self._rect.height()
- corner_case = ((above and on_left) or (above and on_right) or
- (below and on_left) or (below and on_right))
+ corner_case = ((above and on_left) or (above and on_right)
+ or (below and on_left) or (below and on_right))
vertical = abs(anchor.x() - x) > abs(anchor.y() - y)
- x1 = (x + left_of_center * 10 - right_of_center * 20 + corner_case *
- int(not vertical) * (on_left * 10 - on_right * 20))
- y1 = (y + above_center * 10 - below_center * 20 + corner_case *
- vertical * (above * 10 - below * 20))
+ x1 = (x + left_of_center * 10 - right_of_center * 20 + corner_case
+ * int(not vertical) * (on_left * 10 - on_right * 20))
+ y1 = (y + above_center * 10 - below_center * 20 + corner_case
+ * vertical * (above * 10 - below * 20))
point1.setX(x1)
point1.setY(y1)
- x2 = (x + left_of_center * 20 - right_of_center * 10 + corner_case *
- int(not vertical) * (on_left * 20 - on_right * 10))
- y2 = (y + above_center * 20 - below_center * 10 + corner_case *
- vertical * (above * 20 - below * 10))
+ x2 = (x + left_of_center * 20 - right_of_center * 10 + corner_case
+ * int(not vertical) * (on_left * 20 - on_right * 10))
+ y2 = (y + above_center * 20 - below_center * 10 + corner_case
+ * vertical * (above * 20 - below * 10))
point2.setX(x2)
point2.setY(y2)
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
- self.setPos(mapToParent(
+ self.setPos(self.mapToParent(
event.pos() - event.buttonDownPos(Qt.LeftButton)))
event.setAccepted(True)
else:
self._chart = QChart()
self._chart.setMinimumSize(640, 480)
self._chart.setTitle("Hover the line to show callout. Click the line "
- "to make it stay")
+ "to make it stay")
self._chart.legend().hide()
self.series = QLineSeries()
self.series.append(1, 3)
from PySide6.QtCore import QPointF, Qt
from PySide6.QtGui import QColor, QPainter, QPalette
from PySide6.QtWidgets import (QApplication, QMainWindow, QSizePolicy,
- QWidget)
+ QWidget)
from PySide6.QtCharts import (QAreaSeries, QBarSet, QChart, QChartView,
QLineSeries, QPieSeries, QScatterSeries,
QSplineSeries, QStackedBarSeries)
self.value_max = 10
self.value_count = 7
self.data_table = self.generate_random_data(self.list_count,
- self.value_max, self.value_count)
+ self.value_max, self.value_count)
self.ui.setupUi(self)
self.populate_themebox()
# Pie Chart
chart_view = QChartView(self.create_pie_chart())
- chart_view.setSizePolicy(QSizePolicy.Ignored,
- QSizePolicy.Ignored)
+ chart_view.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.ui.gridLayout.addWidget(chart_view, 1, 1)
self.charts.append(chart_view)
self.ui.antialiasCheckBox.setChecked(True)
# Set the colors from the light theme as default ones
- pal = qApp.palette()
+ pal = qApp.palette() # noqa: F821
pal.setColor(QPalette.Window, QColor(0xf0f0f0))
pal.setColor(QPalette.WindowText, QColor(0x404044))
- qApp.setPalette(pal)
+ qApp.setPalette(pal) # noqa: F821
self.update_ui()
for chart_view in self.charts:
chart_view.chart().setTheme(theme)
-
# Set palette colors based on selected theme
if theme == QChart.ChartThemeLight:
set_colors(QColor(0xf0f0f0), QColor(0x404044))
class DonutBreakdownChart(QChart):
def __init__(self, parent=None):
super().__init__(QChart.ChartTypeCartesian,
- parent, Qt.WindowFlags())
+ parent, Qt.WindowFlags())
self.main_series = QPieSeries()
self.main_series.setPieSize(0.7)
self.addSeries(self.main_series)
from PySide6.QtCore import Qt, QRectF, Slot
from PySide6.QtGui import QBrush, QColor, QPainter, QPen
from PySide6.QtWidgets import (QApplication, QDoubleSpinBox,
- QFormLayout, QGridLayout, QGroupBox, QPushButton, QWidget)
+ QFormLayout, QGridLayout, QGroupBox,
+ QPushButton, QWidget)
from PySide6.QtCharts import QBarSeries, QBarSet, QChart, QChartView
def update_legend_layout(self):
legend = self.chart.legend()
- rect = QRectF(self.legend_posx.value(),
- self.legend_posy.value(),
- self.legend_width.value(),
- self.legend_height.value())
+ rect = QRectF(self.legend_posx.value(), self.legend_posy.value(),
+ self.legend_width.value(), self.legend_height.value())
legend.setGeometry(rect)
legend.update()
legend = f'{command} {memory_usage}%'
result.append([legend, memory_usage])
- result.sort(key = lambda x: x[1], reverse=True)
+ result.sort(key=lambda x: x[1], reverse=True)
return result
from PySide6.QtCore import QAbstractTableModel, QModelIndex, QRect, Qt
from PySide6.QtGui import QColor, QPainter
from PySide6.QtWidgets import (QApplication, QGridLayout, QHeaderView,
- QTableView, QWidget)
+ QTableView, QWidget)
from PySide6.QtCharts import QChart, QChartView, QLineSeries, QVXYModelMapper
self.mapper.setModel(self.model)
self.chart.addSeries(self.series)
- # for storing color hex from the series
- seriesColorHex = "#000000"
-
# get the color of the series and use it for showing the mapped area
self.model.add_mapping(self.series.pen().color().name(),
QRect(0, 0, 2, self.model.rowCount()))
set3 = QBarSet("Mary")
set4 = QBarSet("Samantha")
- set0.append([1, 2, 3, 4, 5, 6])
- set1.append([5, 0, 0, 4, 0, 7])
+ set0.append([1, 2, 3, 4, 5, 6])
+ set1.append([5, 0, 0, 4, 0, 7])
set2.append([3, 5, 8, 13, 8, 5])
- set3.append([5, 6, 7, 3, 4, 5])
- set4.append([9, 7, 5, 3, 1, 2])
+ set3.append([5, 6, 7, 3, 4, 5])
+ set4.append([9, 7, 5, 3, 1, 2])
series = QPercentBarSeries()
series.append(set0)
self._selectedPointConfig = self._series.pointConfiguration(index)
selected_point = self._series.at(index)
selected_index_lineedit = self._selected_point_index_lineedit
- selected_index_lineedit.setText("(" + str(selected_point.x()) + ", " +
- str(selected_point.y()) + ")")
+ selected_index_lineedit.setText("(" + str(selected_point.x()) + ", "
+ + str(selected_point.y()) + ")")
config = self._series.pointConfiguration(index)
color = config.get(PointConfig.Color) or self._series.color()
size = config.get(PointConfig.Size) or self._series.markerSize()
- labelVisibility = (config.get(PointConfig.LabelVisibility) or
- self._series.pointLabelsVisible())
+ labelVisibility = (config.get(PointConfig.LabelVisibility)
+ or self._series.pointLabelsVisible())
customLabel = config.get(PointConfig.LabelFormat) or ""
combobox_value_list = [
from PySide6.QtCore import Slot, QPointF, Qt
from PySide6.QtCharts import QChart, QChartView, QSplineSeries
from PySide6.QtGui import QPainter, QImage
-from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QComboBox, QCheckBox, QLabel, QHBoxLayout
+from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
+ QComboBox, QCheckBox, QLabel, QHBoxLayout)
import utilities as Utilities
marker_size = 20.
series = QSplineSeries()
series.append([QPointF(0, 0),
- QPointF(0.5, 2.27),
- QPointF(1.5, 2.2),
- QPointF(3.3, 1.7),
- QPointF(4.23, 3.1),
- QPointF(5.3, 2.3),
- QPointF(6.47, 4.1)])
+ QPointF(0.5, 2.27),
+ QPointF(1.5, 2.2),
+ QPointF(3.3, 1.7),
+ QPointF(4.23, 3.1),
+ QPointF(5.3, 2.3),
+ QPointF(6.47, 4.1)])
series.setMarkerSize(marker_size)
series.setLightMarker(Utilities.default_light_marker(marker_size))
series.setSelectedLightMarker(Utilities.default_selected_light_marker(marker_size))
char_point_combobox.addItems(["Red rectangle", "Green triangle", "Orange circle"])
char_point_combobox.currentIndexChanged.connect(set_light_marker)
-
@Slot(int)
def set_selected_light_marker(index):
- series.setSelectedLightMarker(Utilities.get_selected_point_representation(Utilities.selected_point_type(index), marker_size))
+ series.setSelectedLightMarker(
+ Utilities.get_selected_point_representation(
+ Utilities.selected_point_type(index), marker_size))
char_point_selected = QLabel("Char point selected: ")
char_point_selected_combobox.addItems(["Blue triangle", "Yellow rectangle", "Lavender circle"])
char_point_selected_combobox.currentIndexChanged.connect(set_selected_light_marker)
-
@Slot(int)
def set_line_color(index):
series.setColor(Utilities.make_line_color(Utilities.line_color(index)))
line_color_combobox.addItems(["Blue", "Black", "Mint"])
line_color_combobox.currentIndexChanged.connect(set_line_color)
-
@Slot(int)
def display_unselected_points(checkbox_state):
if checkbox_state:
- series.setLightMarker(Utilities.get_point_representation(Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
+ series.setLightMarker(
+ Utilities.get_point_representation(
+ Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
else:
series.setLightMarker(QImage())
show_unselected_points_checkbox.setChecked(True)
show_unselected_points_checkbox.stateChanged.connect(display_unselected_points)
-
control_label = QLabel("Marker and Selection Controls")
control_label.setAlignment(Qt.AlignHCenter)
control_label_font = control_label.font()
from PySide6.QtGui import QImage, QPainter, QColor
from PySide6.QtCore import Qt
-import rc_markers
+import rc_markers # noqa: F401
+
def rectangle(point_type, image_size):
image = QImage(image_size, image_size, QImage.Format_RGB32)
painter.end()
return image
+
def triangle(point_type, image_size):
return QImage(point_type[3]).scaled(image_size, image_size)
+
def circle(point_type, image_size):
image = QImage(image_size, image_size, QImage.Format_ARGB32)
image.fill(QColor(0, 0, 0, 0))
painter.end()
return image
+
_point_types = [("RedRectangle", rectangle, Qt.red),
("GreenTriangle", triangle, Qt.green, ":/images/green_triangle.png"),
("OrangeCircle", circle, QColor(255, 127, 80))]
("LavenderCircle", circle, QColor(147, 112, 219))]
_line_colors = [("Blue", QColor(65, 105, 225)), ("Black", Qt.black), ("Mint", QColor(70, 203, 155))]
+
def point_type(index):
return _point_types[index]
+
def selected_point_type(index):
return _selected_point_types[index]
+
def line_color(index):
return _line_colors[index]
def default_light_marker(image_size):
return rectangle(_point_types[0], image_size)
+
def default_selected_light_marker(image_size):
return triangle(_selected_point_types[0], image_size)
def get_point_representation(point_type, image_size):
return point_type[1](point_type, image_size)
+
def get_selected_point_representation(point_type, image_size):
return point_type[1](point_type, image_size)
+
def make_line_color(line_color):
return line_color[1]
def detach(self):
if not self._shared_memory.detach():
- self.ui.label.setText(tr("Unable to detach from shared memory."))
+ self.ui.label.setText(self.tr("Unable to detach from shared memory.")) # noqa: F821
import sys
from PySide6.QtCore import (QByteArray, QDate, QDateTime, QDir, QEvent, QPoint,
- QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt, Slot)
+ QRect, QRegularExpression, QSettings, QSize, QTime,
+ QTimer, Qt, Slot)
from PySide6.QtGui import (QAction, QColor, QIcon, QIntValidator,
- QDoubleValidator, QRegularExpressionValidator, QValidator)
+ QDoubleValidator, QRegularExpressionValidator,
+ QValidator)
from PySide6.QtWidgets import (QAbstractItemView, QApplication,
- QCheckBox, QComboBox, QFileDialog, QDialog, QDialogButtonBox, QGridLayout,
- QGroupBox, QHeaderView, QInputDialog, QItemDelegate, QLabel, QLineEdit,
- QMainWindow, QMessageBox, QStyle, QSpinBox, QStyleOptionViewItem,
- QTableWidget, QTableWidgetItem, QTreeWidget, QTreeWidgetItem, QVBoxLayout)
+ QCheckBox, QComboBox, QFileDialog, QDialog,
+ QDialogButtonBox, QGridLayout,
+ QGroupBox, QHeaderView, QInputDialog,
+ QItemDelegate, QLabel, QLineEdit,
+ QMainWindow, QMessageBox, QStyle, QSpinBox,
+ QStyleOptionViewItem, QTableWidget,
+ QTableWidgetItem, QTreeWidget, QTreeWidgetItem,
+ QVBoxLayout)
class TypeChecker:
@Slot()
def open_inifile(self):
file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File",
- '', "INI Files (*.ini *.conf)")
+ '', "INI Files (*.ini *.conf)")
if file_name:
self.load_ini_file(file_name)
@Slot()
def open_property_list(self):
file_name, _ = QFileDialog.getOpenFileName(self,
- "Open Property List", '', "Property List Files (*.plist)")
+ "Open Property List", '',
+ "Property List Files (*.plist)")
if file_name:
settings = QSettings(file_name, QSettings.NativeFormat)
@Slot()
def open_registry_path(self):
path, ok = QInputDialog.getText(self, "Open Registry Path",
- "Enter the path in the Windows registry:",
- QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
+ "Enter the path in the Windows registry:",
+ QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
if ok and path != '':
settings = QSettings(path, QSettings.NativeFormat)
@Slot()
def about(self):
QMessageBox.about(self, "About Settings Editor",
- "The <b>Settings Editor</b> example shows how to access "
- "application settings using Qt.")
-
- def create_actions(self):
- self._open_settings_act = QAction("&Open Application Settings...",
- self, shortcut="Ctrl+O", triggered=self.openSettings)
-
- self._open_ini_file_act = QAction("Open I&NI File...", self,
- shortcut="Ctrl+N", triggered=self.openIniFile)
-
- self._open_property_list_act = QAction("Open macOS &Property List...",
- self, shortcut="Ctrl+P", triggered=self.openPropertyList)
+ "The <b>Settings Editor</b> example shows how to access "
+ "application settings using Qt.")
def create_actions(self):
self.open_settings_action = QAction("&Open Application Settings...",
- self, shortcut="Ctrl+O", triggered=self.open_settings)
+ self, shortcut="Ctrl+O", triggered=self.open_settings)
self.open_ini_file_action = QAction("Open I&NI File...", self,
- shortcut="Ctrl+N", triggered=self.open_inifile)
+ shortcut="Ctrl+N", triggered=self.open_inifile)
- self.open_property_list_action = QAction("Open macOS &Property List...",
- self, shortcut="Ctrl+P", triggered=self.open_property_list)
+ self.open_property_list_action = QAction("Open macOS &Property List...", self,
+ shortcut="Ctrl+P",
+ triggered=self.open_property_list)
if sys.platform != 'darwin':
self.open_property_list_action.setEnabled(False)
self.open_registry_path_action = QAction(
- "Open Windows &Registry Path...", self, shortcut="Ctrl+G",
- triggered=self.open_registry_path)
+ "Open Windows &Registry Path...", self, shortcut="Ctrl+G",
+ triggered=self.open_registry_path)
if sys.platform != 'win32':
self.open_registry_path_action.setEnabled(False)
self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R",
- enabled=False, triggered=self.settings_tree.refresh)
+ enabled=False, triggered=self.settings_tree.refresh)
- self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q",
- triggered=self.close)
+ self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close)
self.auto_refresh_action = QAction("&Auto-Refresh", self,
- shortcut="Ctrl+A", checkable=True, enabled=False)
+ shortcut="Ctrl+A", checkable=True, enabled=False)
self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh)
self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled)
self.fallbacks_action = QAction("&Fallbacks", self,
- shortcut="Ctrl+F", checkable=True, enabled=False)
+ shortcut="Ctrl+F", checkable=True, enabled=False)
self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled)
self.about_action = QAction("&About", self, triggered=self.about)
self.about_Qt_action = QAction("About &Qt", self,
- triggered=qApp.aboutQt)
+ triggered=qApp.aboutQt) # noqa: F821
def create_menus(self):
self.file_menu = self.menuBar().addMenu("&File")
# The signal might not be connected.
try:
self.itemChanged.disconnect(self.update_setting)
- except:
+ except Exception:
pass
self.settings.sync()
key = ancestor.text(0) + '/' + key
ancestor = ancestor.parent()
- d = item.data(2, Qt.UserRole)
self.settings.setValue(key, item.data(2, Qt.UserRole))
if self.auto_refresh:
value = editor.value()
else:
value = self.value_from_lineedit(editor, model, index)
- if not value is None:
+ if value is not None:
model.setData(index, value, Qt.UserRole)
model.setData(index, self.display_text(value), Qt.DisplayRole)
HELP = ("Use mouse wheel or the '+' and '-' keys to zoom. Press and "
- "hold left mouse button to scroll.")
+ "hold left mouse button to scroll.")
class RenderThread(QThread):
self.abort = False
for i in range(RenderThread.colormap_size):
- self.colormap.append(self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size)))
+ self.colormap.append(
+ self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size)))
def stop(self):
self.mutex.lock()
if num_iterations < max_iterations:
image.setPixel(x + half_width, y + half_height,
- self.colormap[num_iterations % RenderThread.colormap_size])
+ self.colormap[
+ num_iterations % RenderThread.colormap_size])
all_black = False
else:
image.setPixel(x + half_width, y + half_height, qRgb(0, 0, 0))
if self.pixmap.isNull():
painter.setPen(Qt.white)
painter.drawText(self.rect(), Qt.AlignCenter,
- "Rendering initial image, please wait...")
+ "Rendering initial image, please wait...")
return
if self._cur_scale == self._pixmap_scale:
painter.setPen(Qt.NoPen)
painter.setBrush(QColor(0, 0, 0, 127))
painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10,
- metrics.lineSpacing() + 5)
+ metrics.lineSpacing() + 5)
painter.setPen(Qt.white)
painter.drawText((self.width() - text_width) / 2,
- metrics.leading() + metrics.ascent(), text)
+ metrics.leading() + metrics.ascent(), text)
def resizeEvent(self, event):
self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
delta_y = (self.height() - self.pixmap.height()) / 2 - self._pixmap_offset.y()
self.scroll(delta_x, delta_y)
- @Slot(QImage,float)
+ @Slot(QImage, float)
def update_pixmap(self, image, scale_factor):
if not self._last_drag_pos.isNull():
return
def zoom(self, zoomFactor):
self._cur_scale *= zoomFactor
self.update()
- self.thread.render(self._center_x, self._center_y, self._cur_scale,
- self.size())
+ self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
def scroll(self, deltaX, deltaY):
self._center_x += deltaX * self._cur_scale
self._center_y += deltaY * self._cur_scale
self.update()
- self.thread.render(self._center_x, self._center_y, self._cur_scale,
- self.size())
+ self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
if __name__ == '__main__':
from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
QLabel, QPushButton, QHBoxLayout, QSizePolicy,
QRadioButton, QSlider, QVBoxLayout, QWidget)
-from PySide6.QtDataVisualization import (QAbstract3DGraph, QAbstract3DSeries,
- Q3DBars)
+from PySide6.QtDataVisualization import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
class BarGraph(QObject):
selectionModeList.addItem("Slice into Row and Item", sel)
sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
selectionModeList.addItem("Slice into Column", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn)
+ sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn)
selectionModeList.addItem("Slice into Column and Item", sel)
- sel = (QAbstract3DGraph.SelectionItemRowAndColumn
- | QAbstract3DGraph.SelectionMultiSeries)
+ sel = (QAbstract3DGraph.SelectionItemRowAndColumn | QAbstract3DGraph.SelectionMultiSeries)
selectionModeList.addItem("Multi: Bar, Row, Col", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndRow
+ sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
| QAbstract3DGraph.SelectionMultiSeries)
selectionModeList.addItem("Multi, Slice: Row, Item", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn
+ sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn
| QAbstract3DGraph.SelectionMultiSeries)
selectionModeList.addItem("Multi, Slice: Col, Item", sel)
selectionModeList.setCurrentIndex(1)
surface = SurfaceGraph()
if (not bars.initialize(minimum_graph_size, screen_size)
- or not scatter.initialize(minimum_graph_size, screen_size)
- or not surface.initialize(minimum_graph_size, screen_size)):
+ or not scatter.initialize(minimum_graph_size, screen_size)
+ or not surface.initialize(minimum_graph_size, screen_size)):
QMessageBox.warning(None, "Graph Gallery", "Couldn't initialize the OpenGL context.")
sys.exit(-1)
# If we have no data or mapping, or the categories are not defined,
# simply clear the array
if (not self._dataSet or not self._mapping
- or not self._mapping.rowCategories()
- or not self._mapping.columnCategories()):
+ or not self._mapping.rowCategories()
+ or not self._mapping.columnCategories()):
self.resetArray()
return
from PySide6.QtQuick import QQuickView
from PySide6.QtDataVisualization import qDefaultSurfaceFormat
-from datasource import DataSource
-import rc_qmlsurfacegallery
+from datasource import DataSource # noqa: F401
+import rc_qmlsurfacegallery # noqa: F401
if __name__ == "__main__":
import sys
from PySide6.QtCore import QCoreApplication
-from PySide6.QtDBus import QDBusConnection, QDBusInterface, QDBusReply
+from PySide6.QtDBus import QDBusConnection, QDBusInterface, QDBusReply
SERVICE_NAME = 'org.example.QtDBus.PingExample'
value = reply.value()
print(f'ping: Reply was: {value}')
sys.exit(0)
-
@Slot(str, result=str)
def ping(self, arg):
print(f'pong: Received ping({arg})')
- qApp.quit()
+ qApp.quit() # noqa: F821
return f'ping("{arg}") got called'
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow,
- QMessageBox, QToolButton)
+from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtCore import (QDir, QFile, QFileInfo, QSettings, Slot)
from ui_mainwindow import Ui_MainWindow
nav = self._pdfView.pageNavigator()
self._pageSelector = QPdfPageSelector(self._toolBar)
self._toolBar.insertWidget(self._uiAssets_forward, self._pageSelector)
- self._pageSelector.setDocument(self._document);
+ self._pageSelector.setDocument(self._document)
self._pageSelector.currentPageChanged.connect(self.pageSelected)
nav.currentPageChanged.connect(self._pageSelector.setCurrentPage)
nav.backAvailableChanged.connect(self._uiAssets_back.setEnabled)
from enum import Enum, auto
-from PySide6.QtCore import QFile, QFileInfo, QObject, QSettings, Signal, Slot
+from PySide6.QtCore import QFileInfo, QObject, QSettings, Signal, Slot
DEFAULT_MAX_FILES = 10
list = []
for name, viewer in self._viewers.items():
if ((self._defaultViewer and viewer.isDefaultViewer())
- or (not self._defaultViewer and name == "TxtViewer")):
+ or (not self._defaultViewer and name == "TxtViewer")):
name += "(default)"
list.append(name)
return list
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from tictactoe import TicTacToe
+from tictactoe import TicTacToe # noqa: F401
from tictactoeplugin import TicTacToePlugin
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
for position in range(0, 8, 3):
if (self._state[position] != EMPTY
- and self._state[position + 1] == self._state[position]
- and self._state[position + 2] == self._state[position]):
+ and self._state[position + 1] == self._state[position]
+ and self._state[position + 2] == self._state[position]):
y = self._cell_rect(position).center().y()
painter.drawLine(0, y, self.width(), y)
self._turn_number = 9
for position in range(3):
if (self._state[position] != EMPTY
- and self._state[position + 3] == self._state[position]
- and self._state[position + 6] == self._state[position]):
+ and self._state[position + 3] == self._state[position]
+ and self._state[position + 6] == self._state[position]):
x = self._cell_rect(position).center().x()
painter.drawLine(x, 0, x, self.height())
self._turn_number = 9
if (self._state[0] != EMPTY and self._state[4] == self._state[0]
- and self._state[8] == self._state[0]):
+ and self._state[8] == self._state[0]):
painter.drawLine(0, 0, self.width(), self.height())
self._turn_number = 9
if (self._state[2] != EMPTY and self._state[4] == self._state[2]
- and self._state[6] == self._state[2]):
+ and self._state[6] == self._state[2]):
painter.drawLine(0, self.height(), self.width(), 0)
self._turn_number = 9
from tictactoetaskmenu import TicTacToeTaskMenuFactory
from PySide6.QtGui import QIcon
-from PySide6.QtDesigner import QDesignerCustomWidgetInterface
+from PySide6.QtDesigner import QDesignerCustomWidgetInterface
DOM_XML = """
# Main menu bar
self.menu = self.menuBar()
self.menu_file = self.menu.addMenu("File")
- exit = QAction("Exit", self, triggered=qApp.quit)
+ exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821
self.menu_file.addAction(exit)
self.menu_about = self.menu.addMenu("&About")
about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
- triggered=qApp.aboutQt)
+ triggered=qApp.aboutQt) # noqa: F821
self.menu_about.addAction(about)
# Figure (Left)
# Main menu bar
self.menu = self.menuBar()
self.menu_file = self.menu.addMenu("File")
- exit = QAction("Exit", self, triggered=qApp.quit)
+ exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821
self.menu_file.addAction(exit)
self.menu_about = self.menu.addMenu("&About")
about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
- triggered=qApp.aboutQt)
+ triggered=qApp.aboutQt) # noqa: F821
self.menu_about.addAction(about)
# Create a label for the display camera
# Main menu bar
self.menu = self.menuBar()
self.menu_file = self.menu.addMenu("File")
- exit = QAction("Exit", self, triggered=qApp.quit)
+ exit = QAction("Exit", self, triggered=qApp.quit) # noqa: F821
self.menu_file.addAction(exit)
self.menu_about = self.menu.addMenu("&About")
about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
- triggered=qApp.aboutQt)
+ triggered=qApp.aboutQt) # noqa: F821
self.menu_about.addAction(about)
# Create an artificial color close to the original one
"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
-import os
import sys
from PySide6.QtCore import QSize
# If we have no data or mapping, or the categories are not defined,
# simply clear the array
if (not self._dataSet or not self._mapping
- or not self._mapping.rowCategories()
- or not self._mapping.columnCategories()):
+ or not self._mapping.rowCategories()
+ or not self._mapping.columnCategories()):
self.resetArray()
return
import sys
from PySide6.QtCore import QPoint, QTimer, QTime, Qt
-from PySide6.QtGui import (QColor, QGradient, QGuiApplication, QPainter,
- QPalette, QPolygon, QRasterWindow)
+from PySide6.QtGui import QGuiApplication, QPainter, QPalette, QPolygon, QRasterWindow
"""Simplified PySide6 port of the gui/analogclock example from Qt v6.x"""
self._seconds_hand = QPolygon([QPoint(1, 14), QPoint(-1, 14),
QPoint(-1, -89), QPoint(1, -89)])
- palette = qApp.palette()
+ palette = qApp.palette() # noqa: F821
self._background_color = palette.color(QPalette.Window)
self._hour_color = palette.color(QPalette.Text)
self._minute_color = palette.color(QPalette.Text)
painter.drawConvexPolygon(self._hour_hand)
painter.restore()
- for i in range(0, 12):
+ for _ in range(0, 12):
painter.drawRect(73, -3, 16, 6)
painter.rotate(30.0)
painter.setPen(self._minute_color)
- for j in range(0, 60):
+ for _ in range(0, 60):
painter.drawLine(92, 0, 96, 0)
painter.rotate(6.0)
from PySide6.QtGui import QGuiApplication, QRhi, QSurfaceFormat
from rhiwindow import HelloWindow
-import rc_rhiwindow
+import rc_rhiwindow # noqa: F401
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
import sys
from PySide6.QtCore import (QEvent, QFile, QIODevice, QPointF, QRectF, QSize,
- QSizeF, qFatal, qWarning, Qt)
+ qFatal, qWarning, Qt)
from PySide6.QtGui import (QColor, QFont, QGradient, QImage, QMatrix4x4,
- QOffscreenSurface, QPainter, QPlatformSurfaceEvent,
- QSurface, QWindow)
-from PySide6.QtGui import (QRhi, QRhiBuffer, QRhiCommandBuffer,
+ QPainter, QPlatformSurfaceEvent, QSurface, QWindow)
+from PySide6.QtGui import (QRhi, QRhiBuffer,
QRhiDepthStencilClearValue,
QRhiGraphicsPipeline, QRhiNullInitParams,
QRhiGles2InitParams, QRhiRenderBuffer,
# Y up (note clipSpaceCorrMatrix in m_viewProjection), CCW
VERTEX_DATA = numpy.array([
- 0.0, 0.5, 1.0, 0.0, 0.0,
- -0.5, -0.5, 0.0, 1.0, 0.0,
- 0.5, -0.5, 0.0, 0.0, 1.0], dtype=numpy.float32)
+ 0.0, 0.5, 1.0, 0.0, 0.0,
+ -0.5, -0.5, 0.0, 1.0, 0.0,
+ 0.5, -0.5, 0.0, 0.0, 1.0], dtype=numpy.float32)
UBUF_SIZE = 68
surfaceSize = self.m_sc.surfacePixelSize() if self.m_hasSwapChain else QSize()
# stop pushing frames when not exposed (or size is 0)
- if (not is_exposed or (self.m_hasSwapChain and surfaceSize.isEmpty())) and self.m_initialized and not self.m_notExposed:
+ if ((not is_exposed or (self.m_hasSwapChain and surfaceSize.isEmpty()))
+ and self.m_initialized and not self.m_notExposed):
self.m_notExposed = True
# Continue when exposed again and the surface has a valid size. Note
super().__init__()
self.hello = ["Hallo Welt", "你好,世界", "Hei maailma",
- "Hola Mundo", "Привет мир"]
+ "Hola Mundo", "Привет мир"]
self.button = QPushButton("Click me!")
self.text = QLabel(f"Hello World auto_quit={auto_quit}")
Map Viewer Example
==================
+.. tags:: Android
+
The Map Viewer example shows how to display and interact with a map,
search for an address, and find driving directions.
sample_size = fmt.bytesPerSample() * 8
if sample_size == 8:
if fmt.sampleFormat() == QAudioFormat.UInt8:
- scaler = lambda x: ((1.0 + x) / 2 * 255)
+ scaler = lambda x: ((1.0 + x) / 2 * 255) # noqa: E731
pack_format = 'B'
elif fmt.sampleFormat() == QAudioFormat.Int16:
- scaler = lambda x: x * 127
+ scaler = lambda x: x * 127 # noqa: E731
pack_format = 'b'
elif sample_size == 16:
little_endian = QSysInfo.ByteOrder == QSysInfo.LittleEndian
if fmt.sampleFormat() == QAudioFormat.UInt8:
- scaler = lambda x: (1.0 + x) / 2 * 65535
+ scaler = lambda x: (1.0 + x) / 2 * 65535 # noqa: E731
pack_format = '<H' if little_endian else '>H'
elif fmt.sampleFormat() == QAudioFormat.Int16:
- scaler = lambda x: x * 32767
+ scaler = lambda x: x * 32767 # noqa: E731
pack_format = '<h' if little_endian else '>h'
- assert(pack_format != '')
+ assert pack_format != ''
channel_bytes = fmt.bytesPerSample()
- sample_bytes = fmt.channelCount() * channel_bytes
length = (fmt.sampleRate() * fmt.channelCount() * channel_bytes) * durationUs // 100000
layout.addWidget(self.m_modeButton)
- self.m_suspendResumeButton = QPushButton(
- clicked=self.toggle_suspend_resume)
+ self.m_suspendResumeButton = QPushButton(clicked=self.toggle_suspend_resume)
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
layout.addWidget(self.m_suspendResumeButton)
volume_box = QHBoxLayout()
volume_label = QLabel("Volume:")
- self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
- singleStep=10)
+ self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100, singleStep=10)
self.m_volumeSlider.valueChanged.connect(self.volume_changed)
volume_box.addWidget(volume_label)
qWarning("Default format not supported - trying to use nearest")
self.m_format = info.nearestFormat(self.m_format)
- self.m_generator = Generator(self.m_format,
- self.DURATION_SECONDS * 1000000, self.TONE_SAMPLE_RATE_HZ, self)
+ self.m_generator = Generator(self.m_format, self.DURATION_SECONDS * 1000000,
+ self.TONE_SAMPLE_RATE_HZ, self)
self.create_audio_output()
Audio Output Example
====================
+.. tags:: Android
+
Audio Output demonstrates the basic use cases of QAudioOutput.
This example provides a tone generator to supply continuous audio playback. The
import PySide6
from PySide6.QtCore import QByteArray, QMargins, Qt, Slot, qWarning
from PySide6.QtGui import QPainter, QPalette
-from PySide6.QtMultimedia import (
- QAudio,
- QAudioDevice,
- QAudioFormat,
- QAudioSource,
- QMediaDevices,
-)
-from PySide6.QtWidgets import (
- QApplication,
- QComboBox,
- QPushButton,
- QSlider,
- QVBoxLayout,
- QWidget,
- QLabel
-)
+from PySide6.QtMultimedia import QAudio, QAudioDevice, QAudioFormat, QAudioSource, QMediaDevices
+from PySide6.QtWidgets import (QApplication, QComboBox, QPushButton, QSlider, QVBoxLayout,
+ QWidget, QLabel)
is_android = os.environ.get('ANDROID_ARGUMENT')
if is_android:
- from PySide6.QtCore import QCoreApplication, QMicrophonePermission
+ from PySide6.QtCore import QMicrophonePermission
+
class AudioInfo:
def __init__(self, format: QAudioFormat):
def initialize(self):
if is_android:
permission = QMicrophonePermission()
- permission_status = qApp.checkPermission(permission)
+ permission_status = qApp.checkPermission(permission) # noqa: F821
if permission_status == Qt.PermissionStatus.Undetermined:
- qApp.requestPermission(permission, self, self.initialize)
+ qApp.requestPermission(permission, self, self.initialize) # noqa: F821
return
if permission_status == Qt.PermissionStatus.Denied:
qWarning("Microphone permission is not granted!")
Audio Source Example
====================
+.. tags:: Android
+
A Python application that demonstrates the analogous example in C++
`Audio Source Example <https://doc-snapshots.qt.io/qt6-dev/qtmultimedia-multimedia-audiosource-example.html>`_
from videosettings import VideoSettings, is_android
if is_android:
- from PySide6.QtCore import QCoreApplication, QMicrophonePermission, QCameraPermission
+ from PySide6.QtCore import QMicrophonePermission, QCameraPermission
from ui_camera_mobile import Ui_Camera
else:
from ui_camera import Ui_Camera
image = Path(__file__).parent / "shutter.svg"
self._ui.takeImageButton.setIcon(QIcon(os.fspath(image)))
if not is_android:
- self._ui.actionAbout_Qt.triggered.connect(qApp.aboutQt)
+ self._ui.actionAbout_Qt.triggered.connect(qApp.aboutQt) # noqa: F821
# disable all buttons by default
self.updateCameraActive(False)
if is_android:
# camera
cam_permission = QCameraPermission()
- cam_permission_status = qApp.checkPermission(cam_permission)
+ cam_permission_status = qApp.checkPermission(cam_permission) # noqa: F821
if cam_permission_status == Qt.PermissionStatus.Undetermined:
- qApp.requestPermission(cam_permission, self, self.initialize)
+ qApp.requestPermission(cam_permission, self, self.initialize) # noqa: F821
return
if cam_permission_status == Qt.PermissionStatus.Denied:
qWarning("Camera permission is not granted!")
# microphone
microphone_permission = QMicrophonePermission()
- microphone_permission_status = qApp.checkPermission(microphone_permission)
+ microphone_permission_status = qApp.checkPermission(microphone_permission) # noqa: F821
if microphone_permission_status == Qt.PermissionStatus.Undetermined:
- qApp.requestPermission(microphone_permission, self, self.initialize)
+ qApp.requestPermission(microphone_permission, self, self.initialize) # noqa: F821
return
if microphone_permission_status == Qt.PermissionStatus.Denied:
qWarning("Microphone permission is not granted!")
Camera Example
===============
+.. tags:: Android
+
The Camera Example shows how to use the API to capture a still image or video.
The Camera Example demonstrates how you can use Qt Multimedia to implement some
from ui_videosettings import Ui_VideoSettingsUi
-
def box_value(box):
idx = box.currentIndex()
return None if idx == -1 else box.itemData(idx)
from PySide6.QtCore import QStandardPaths, Qt, Slot
from PySide6.QtGui import QAction, QIcon, QKeySequence
from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
- QMainWindow, QSlider, QStyle, QToolBar)
+ QMainWindow, QSlider, QStyle, QToolBar)
from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,
QMediaPlayer)
from PySide6.QtMultimediaWidgets import QVideoWidget
tool_bar.addWidget(self._volume_slider)
about_menu = self.menuBar().addMenu("&About")
- about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt)
+ about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt) # noqa: F821
about_menu.addAction(about_qt_act)
self._video_widget = QVideoWidget()
@Slot("QMediaPlayer::PlaybackState")
def update_buttons(self, state):
media_count = len(self._playlist)
- self._play_action.setEnabled(media_count > 0
- and state != QMediaPlayer.PlayingState)
+ self._play_action.setEnabled(media_count > 0 and state != QMediaPlayer.PlayingState)
self._pause_action.setEnabled(state == QMediaPlayer.PlayingState)
self._stop_action.setEnabled(state != QMediaPlayer.StoppedState)
self._previous_action.setEnabled(self._player.position() > 0)
def __init__(self, parent=None):
super().__init__(parent)
- app = qApp
+ app = qApp # noqa: F821
app.screenAdded.connect(self.screens_changed)
app.screenRemoved.connect(self.screens_changed)
app.primaryScreenChanged.connect(self.screens_changed)
"""PySide6 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt"""
from PySide6.QtCore import (Signal, QDataStream, QMutex, QMutexLocker,
- QThread, QWaitCondition)
+ QThread, QWaitCondition)
from PySide6.QtGui import QIntValidator
from PySide6.QtWidgets import (QApplication, QDialogButtonBox, QGridLayout,
- QLabel, QLineEdit, QMessageBox, QPushButton, QWidget)
+ QLabel, QLineEdit, QMessageBox, QPushButton,
+ QWidget)
from PySide6.QtNetwork import (QAbstractSocket, QHostAddress, QNetworkInterface,
- QTcpSocket)
+ QTcpSocket)
class FortuneThread(QThread):
port_label.setBuddy(self._port_line_edit)
self._status_label = QLabel(
- "This example requires that you run the Fortune Server example as well.")
+ "This example requires that you run the Fortune Server example as well.")
self._status_label.setWordWrap(True)
self._get_fortune_button = QPushButton("Get Fortune")
def request_new_fortune(self):
self._get_fortune_button.setEnabled(False)
self.thread.request_new_fortune(self._host_line_edit.text(),
- int(self._port_line_edit.text()))
+ int(self._port_line_edit.text()))
def show_fortune(self, nextFortune):
if nextFortune == self._current_fortune:
def display_error(self, socketError, message):
if socketError == QAbstractSocket.HostNotFoundError:
QMessageBox.information(self, "Blocking Fortune Client",
- "The host was not found. Please check the host and port "
- "settings.")
+ "The host was not found. Please check the host and port "
+ "settings.")
elif socketError == QAbstractSocket.ConnectionRefusedError:
QMessageBox.information(self, "Blocking Fortune Client",
- "The connection was refused by the peer. Make sure the "
- "fortune server is running, and check that the host name "
- "and port settings are correct.")
+ "The connection was refused by the peer. Make sure the "
+ "fortune server is running, and check that the host name "
+ "and port settings are correct.")
else:
QMessageBox.information(self, "Blocking Fortune Client",
- f"The following error occurred: {message}.")
+ f"The following error occurred: {message}.")
self._get_fortune_button.setEnabled(True)
def enable_get_fortune_button(self):
- self._get_fortune_button.setEnabled(self._host_line_edit.text() != '' and
- self._port_line_edit.text() != '')
+ self._get_fortune_button.setEnabled(self._host_line_edit.text() != ''
+ and self._port_line_edit.text() != '')
if __name__ == '__main__':
self.link_box.setPlaceholderText("Download Link ...")
self._open_folder_action = self.dest_box.addAction(
- qApp.style().standardIcon(QStyle.SP_DirOpenIcon), QLineEdit.TrailingPosition
+ qApp.style().standardIcon(QStyle.SP_DirOpenIcon), # noqa: F821
+ QLineEdit.TrailingPosition
)
self._open_folder_action.triggered.connect(self.on_open_folder)
port_label.setBuddy(self._port_line_edit)
self._status_label = QLabel("This examples requires that you run "
- "the Fortune Server example as well.")
+ "the Fortune Server example as well.")
self._get_fortune_button = QPushButton("Get Fortune")
self._get_fortune_button.setDefault(True)
quit_button = QPushButton("Quit")
button_box = QDialogButtonBox()
- button_box.addButton(self._get_fortune_button,
- QDialogButtonBox.ActionRole)
+ button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
self._tcp_socket = QTcpSocket(self)
self._block_size = 0
self._tcp_socket.abort()
self._tcp_socket.connectToHost(self._host_line_edit.text(),
- int(self._port_line_edit.text()))
+ int(self._port_line_edit.text()))
def read_fortune(self):
instr = QDataStream(self._tcp_socket)
pass
elif socketError == QAbstractSocket.HostNotFoundError:
QMessageBox.information(self, "Fortune Client",
- "The host was not found. Please check the host name and "
- "port settings.")
+ "The host was not found. Please check the host name and "
+ "port settings.")
elif socketError == QAbstractSocket.ConnectionRefusedError:
QMessageBox.information(self, "Fortune Client",
- "The connection was refused by the peer. Make sure the "
- "fortune server is running, and check that the host name "
- "and port settings are correct.")
+ "The connection was refused by the peer. Make sure the "
+ "fortune server is running, and check that the host name "
+ "and port settings are correct.")
else:
reason = self._tcp_socket.errorString()
QMessageBox.information(self, "Fortune Client",
- f"The following error occurred: {reason}.")
+ f"The following error occurred: {reason}.")
self._get_fortune_button.setEnabled(True)
def enable_get_fortune_button(self):
- self._get_fortune_button.setEnabled(bool(self._host_line_edit.text() and
- self._port_line_edit.text()))
+ self._get_fortune_button.setEnabled(bool(self._host_line_edit.text()
+ and self._port_line_edit.text()))
if __name__ == '__main__':
if not self._tcp_server.listen():
reason = self._tcp_server.errorString()
QMessageBox.critical(self, "Fortune Server",
- f"Unable to start the server: {reason}.")
+ f"Unable to start the server: {reason}.")
self.close()
return
port = self._tcp_server.serverPort()
status_label.setText(f"The server is running on port {port}.\nRun the "
- "Fortune Client example now.")
+ "Fortune Client example now.")
self.fortunes = (
- "You've been leading a dog's life. Stay off the furniture.",
- "You've got to think about tomorrow.",
- "You will be surprised by a loud noise.",
- "You will feel hungry again in another hour.",
- "You might have mail.",
- "You cannot kill time without injuring eternity.",
- "Computers are not intelligent. They only think they are.")
+ "You've been leading a dog's life. Stay off the furniture.",
+ "You've got to think about tomorrow.",
+ "You will be surprised by a loud noise.",
+ "You will feel hungry again in another hour.",
+ "You might have mail.",
+ "You cannot kill time without injuring eternity.",
+ "Computers are not intelligent. They only think they are.")
quit_button.clicked.connect(self.close)
self._tcp_server.newConnection.connect(self.send_fortune)
@Slot(QNetworkReply)
def handle_network_data(self, network_reply: QNetworkReply):
- url = network_reply.url()
if network_reply.error() == QNetworkReply.NoError:
choices: List[str] = []
response: QByteArray = network_reply.readAll()
- xml = QXmlStreamReader(response)
+ xml = QXmlStreamReader(str(response))
while not xml.atEnd():
xml.readNext()
if xml.tokenType() == QXmlStreamReader.StartElement:
import random
from PySide6.QtCore import (Signal, QByteArray, QDataStream, QIODevice,
- QThread, Qt)
+ QThread, Qt)
from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel,
- QMessageBox, QPushButton, QVBoxLayout)
+ QMessageBox, QPushButton, QVBoxLayout)
from PySide6.QtNetwork import (QHostAddress, QNetworkInterface, QTcpServer,
- QTcpSocket)
+ QTcpSocket)
class FortuneThread(QThread):
if not self.server.listen():
reason = self.server.errorString()
QMessageBox.critical(self, "Threaded Fortune Server",
- f"Unable to start the server: {reason}.")
+ f"Unable to start the server: {reason}.")
self.close()
return
port = self.server.serverPort()
status_label.setText(f"The server is running on\n\nIP: {ip_address}\nport: {port}\n\n"
- "Run the Fortune Client example now.")
+ "Run the Fortune Client example now.")
quit_button.clicked.connect(self.close)
document = QJsonDocument.fromJson(json)
root_object = document.object()
kind = root_object["kind"]
- assert(kind == "Listing")
+ assert kind == "Listing"
data_object = root_object["data"]
children_array = data_object["children"]
if not children_array:
HOT_URL = "https://oauth.reddit.com/hot"
LIVE_THREADS_URL = "https://oauth.reddit.com/live/XXXX/about.json"
+
class RedditWrapper(QObject):
authenticated = Signal()
json = reply.readAll()
document = QJsonDocument.fromJson(json)
- assert(document.isObject())
+ assert document.isObject()
root_object = document.object()
data_object = root_object["data"]
websocketUrl = QUrl(data_object["websocket_url"])
from PySide6.QtOpenGL import (QOpenGLBuffer, QOpenGLShader,
QOpenGLShaderProgram, QOpenGLVertexArrayObject)
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMessageBox, QPlainTextEdit,
- QWidget)
+ QWidget)
from PySide6.support import VoidPtr
try:
from OpenGL import GL
except ImportError:
app = QApplication(sys.argv)
message_box = QMessageBox(QMessageBox.Critical, "ContextInfo",
- "PyOpenGL must be installed to run this example.",
- QMessageBox.Close)
+ "PyOpenGL must be installed to run this example.", QMessageBox.Close)
message_box.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate")
message_box.exec()
sys.exit(1)
def print_surface_format(surface_format):
- profile_name = 'core' if surface_format.profile() == QSurfaceFormat.CoreProfile else 'compatibility'
+ if surface_format.profile() == QSurfaceFormat.CoreProfile:
+ profile_name = 'core'
+ else:
+ profile_name = 'compatibility'
major = surface_format.majorVersion()
minor = surface_format.minorVersion()
return f"{profile_name} version {major}.{minor}"
# concept 3.2+ has. This may still fail since version 150 (3.2) is
# specified in the sources but it's worth a try.
if (fmt.renderableType() == QSurfaceFormat.OpenGL and fmt.majorVersion() == 3
- and fmt.minorVersion() <= 1):
+ and fmt.minorVersion() <= 1):
use_new_style_shader = not fmt.testOption(QSurfaceFormat.DeprecatedFunctions)
vertex_shader = vertex_shader_source if use_new_style_shader else vertex_shader_source_110
- fragment_shader = fragment_shader_source if use_new_style_shader else fragment_shader_source_110
+ fragment_shader = (fragment_shader_source
+ if use_new_style_shader
+ else fragment_shader_source_110)
if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertex_shader):
log = self.program.log()
raise Exception("Vertex shader could not be added: {log} ({vertexShader})")
menuWindow.addAction("Add new", QKeySequence(Qt.CTRL | Qt.Key_N),
self.onAddNew)
menuWindow.addAction("Quit", QKeySequence(Qt.CTRL | Qt.Key_Q),
- qApp.closeAllWindows)
+ qApp.closeAllWindows) # noqa: F821
self.onAddNew()
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtCore import Slot, Qt
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMainWindow,
+from PySide6.QtWidgets import (QHBoxLayout, QMainWindow,
QMessageBox, QPushButton, QSlider,
QVBoxLayout, QWidget)
def _main_window():
- for t in qApp.topLevelWidgets():
+ for t in qApp.topLevelWidgets(): # noqa: F821
if isinstance(t, QMainWindow):
return t
return None
messageBox.exec()
sys.exit(1)
-import textures_rc
+import textures_rc # noqa: F401
class GLWidget(QOpenGLWidget):
refCount = 0
coords = (
- ( ( +1, -1, -1 ), ( -1, -1, -1 ), ( -1, +1, -1 ), ( +1, +1, -1 ) ),
- ( ( +1, +1, -1 ), ( -1, +1, -1 ), ( -1, +1, +1 ), ( +1, +1, +1 ) ),
- ( ( +1, -1, +1 ), ( +1, -1, -1 ), ( +1, +1, -1 ), ( +1, +1, +1 ) ),
- ( ( -1, -1, -1 ), ( -1, -1, +1 ), ( -1, +1, +1 ), ( -1, +1, -1 ) ),
- ( ( +1, -1, +1 ), ( -1, -1, +1 ), ( -1, -1, -1 ), ( +1, -1, -1 ) ),
- ( ( -1, -1, +1 ), ( +1, -1, +1 ), ( +1, +1, +1 ), ( -1, +1, +1 ) )
+ ((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
+ ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
+ ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
+ ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)),
+ ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)),
+ ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1))
)
clicked = Signal()
mainLayout.addWidget(glw, i, j)
glw.clicked.connect(self.setCurrentGlWidget)
- qApp.lastWindowClosed.connect(glw.freeGLResources)
+ qApp.lastWindowClosed.connect(glw.freeGLResources) # noqa: F821
self.currentGlWidget = self.glWidgets[0][0]
if self._exiting:
return
- assert(ctx.thread() == QThread.currentThread())
+ assert ctx.thread() == QThread.currentThread()
# Make the context (and an offscreen surface) current for self thread.
# The QOpenGLWidget's fbo is bound in the context.
from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import QCoreApplication, QUrl
-import rc_viewer
+import rc_viewer # noqa: F401
"""PySide6 port of the pdf/pdfviewer example from Qt v6.x"""
QAbstractListModel in QML
=========================
+.. tags:: Android
+
This example shows how to add, remove and move items inside a QML
ListView, but showing and editing the data via roles using a
QAbstractListModel from Python.
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
-from model import BaseModel
+from model import BaseModel # noqa: F401
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from PySide6.QtCore import (QAbstractListModel, QByteArray, QModelIndex, Qt,
- Slot)
+from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot
from PySide6.QtGui import QColor
from PySide6.QtQml import QmlElement
self.beginMoveRows(QModelIndex(), sourceRow, sourceRow + count, QModelIndex(), end)
# start database work
- pops = self.db[sourceRow : sourceRow + count + 1]
+ pops = self.db[sourceRow: sourceRow + count + 1]
if sourceRow > dstChild:
self.db = (
self.db[:dstChild]
+ pops
+ self.db[dstChild:sourceRow]
- + self.db[sourceRow + count + 1 :]
+ + self.db[sourceRow + count + 1:]
)
else:
start = self.db[:sourceRow]
- middle = self.db[dstChild : dstChild + 1]
- endlist = self.db[dstChild + count + 1 :]
+ middle = self.db[dstChild: dstChild + 1]
+ endlist = self.db[dstChild + count + 1:]
self.db = start + middle + pops + endlist
# end database work
self.beginRemoveRows(QModelIndex(), row, row + count)
# start database work
- self.db = self.db[:row] + self.db[row + count + 1 :]
+ self.db = self.db[:row] + self.db[row + count + 1:]
# end database work
self.endRemoveRows()
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Person
+from person import Person # noqa: F401
if __name__ == '__main__':
@shoe_size.setter
def shoe_size(self, s):
self._shoe_size = s
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
app = QCoreApplication(sys.argv)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example
+ from Qt v6.x"""
from pathlib import Path
-import os
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
app = QCoreApplication(sys.argv)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example
+ from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
app = QCoreApplication(sys.argv)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example
+ from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example
+ from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
app = QCoreApplication(sys.argv)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example from Qt v6.x"""
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example
+ from Qt v6.x"""
from pathlib import Path
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
-from person import Boy, Girl
+from person import Boy, Girl # noqa: F401
from birthdayparty import BirthdayParty
-from happybirthdaysong import HappyBirthdaySong
+from happybirthdaysong import HappyBirthdaySong # noqa: F401
app = QCoreApplication(sys.argv)
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
-from happybirthdaysong import HappyBirthdaySong
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+from happybirthdaysong import HappyBirthdaySong # noqa: F401
if __name__ == "__main__":
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
app = QCoreApplication(sys.argv)
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlComponent, QQmlEngine
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
if __name__ == '__main__':
@shoe_size.setter
def shoe_size(self, s):
self._shoe_size = s
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example from Qt v5.x"""
+"""PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example
+ from Qt v5.x"""
import os
from pathlib import Path
pen = QPen(self._color, 2)
painter.setPen(pen)
painter.setRenderHints(QPainter.Antialiasing, True)
- painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
+ painter.drawPie(
+ self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
@QmlElement
QML_IMPORT_NAME = "Charts"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class PieChart(QQuickItem):
def __init__(self, parent=None):
QML_IMPORT_NAME = "Charts"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class PieSlice(QQuickPaintedItem):
def __init__(self, parent=None):
Using Model Example
===================
+.. tags:: Android
+
A Python application that demonstrates how to use a :ref:`QAbstractListModel`
with QML.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import os
+from dataclasses import dataclass
from pathlib import Path
import sys
from PySide6.QtCore import QAbstractListModel, Qt, QUrl, QByteArray
from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView
-from PySide6.QtQml import qmlRegisterSingletonType
+from PySide6.QtQml import QmlElement, QmlSingleton
+QML_IMPORT_NAME = "PersonModel"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@dataclass
+class Person:
+ name: str
+ myrole: str
+
+
+@QmlElement
+@QmlSingleton
class PersonModel (QAbstractListModel):
MyRole = Qt.UserRole + 1
- def __init__(self, parent=None):
- QAbstractListModel.__init__(self, parent)
- self._data = []
+ def __init__(self, data, parent=None):
+ super().__init__(parent)
+ self._data = data
def roleNames(self):
roles = {
- PersonModel.MyRole: QByteArray(b'modelData'),
+ PersonModel.MyRole: QByteArray(b'myrole'),
Qt.DisplayRole: QByteArray(b'display')
}
return roles
def data(self, index, role):
d = self._data[index.row()]
-
if role == Qt.DisplayRole:
- return d['name']
- elif role == Qt.DecorationRole:
+ return d.name
+ if role == Qt.DecorationRole:
return Qt.black
- elif role == PersonModel.MyRole:
- return d['myrole']
+ if role == PersonModel.MyRole:
+ return d.myrole
return None
- def populate(self, data=None):
- for item in data:
- self._data.append(item)
-
-
-def model_callback(engine):
- my_model = PersonModel()
- data = [{'name': 'Qt', 'myrole': 'role1'},
- {'name': 'PySide', 'myrole': 'role2'}]
- my_model.populate(data)
- return my_model
+ @staticmethod
+ def create(engine):
+ data = [Person('Qt', 'myrole'), Person('PySide', 'role2')]
+ return PersonModel(data)
if __name__ == '__main__':
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
- qmlRegisterSingletonType(PersonModel, "PersonModel", 1, 0, "MyModel", model_callback)
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Error:
width: 100
height: 100
anchors.fill: parent
- model: MyModel
+ model: PersonModel
delegate: Component {
Rectangle {
height: 25
width: 100
Text {
- function displayText() {
- var result = ""
- if (typeof display !== "undefined")
- result = display + ": "
- result += modelData
- return result
- }
-
- text: displayText()
+ text: display + ": " + myrole
}
}
}
Object List Model Example
=========================
+.. tags:: Android
+
A list of QObject values can also be used as a model.
A list[QObject,] provides the properties of the objects in the list as roles.
# This example illustrates exposing a list of QObjects as a model in QML
+
class DataObject(QObject):
nameChanged = Signal()
def setName(self, name):
if name != self._name:
self._name = name
- nameChanged.emit()
+ self.nameChanged.emit()
def color(self):
return self._color
def setColor(self, color):
if color != self._color:
self._color = color
- colorChanged.emit()
-
+ self.colorChanged.emit()
name = Property(str, name, setName, notify=nameChanged)
color = Property(str, color, setColor, notify=colorChanged)
String List Model Example
=========================
+.. tags:: Android
+
A model may be a simple 'list',
which provides the contents of the list via the modelData role.
dataList = ["Item 1", "Item 2", "Item 3", "Item 4"]
view = QQuickView()
- view.setInitialProperties({"model": dataList })
+ view.setInitialProperties({"model": dataList})
qml_file = Path(__file__).parent / "view.qml"
view.setSource(QUrl.fromLocalFile(qml_file))
Scene Graph Painted Item Example
================================
+.. tags:: Android
+
Shows how to implement QPainter-based custom scenegraph items.
The Painted Item example shows how to use the QML Scene Graph framework to
-0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5,
-0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5,
- 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
- -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
- -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
- 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5],
+ 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
+ -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
+ -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
+ 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5],
dtype=numpy.float32)
-TEX_COORDS = numpy.array([0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
- 1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
- 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
- 0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
+TEX_COORDS = numpy.array([0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
+ 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
+ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
- 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
- 0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
- 0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
- 1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
+ 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
+ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
- 0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
- 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
- 1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
- 0.0, 1.0, 0.0, 0.0, 1.0, 1.0], dtype=numpy.float32)
+ 0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
+ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
+ 1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0, 1.0, 1.0], dtype=numpy.float32)
class CubeRenderer():
self.m_vao = QOpenGLVertexArrayObject()
self.m_vao.create()
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.m_vao)
self.m_vbo = QOpenGLBuffer()
self.m_vbo.create()
f.glEnable(GL_DEPTH_TEST)
self.m_program.bind()
- vaoBinder = QOpenGLVertexArrayObject.Binder(self.m_vao)
# If VAOs are not supported, set the vertex attributes every time.
if not self.m_vao.isCreated():
self.setupVertexAttribs()
# If self is a resize after the scene is up and running, recreate the
# texture and the Quick item and scene.
if (self.texture_id()
- and self.m_textureSize != self.size() * self.devicePixelRatio()):
+ and self.m_textureSize != self.size() * self.devicePixelRatio()):
self.resizeTexture()
@Slot()
from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView, QQuickWindow, QSGRendererInterface
-from squircle import Squircle
+from squircle import Squircle # noqa: F401
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
from PySide6.QtQuick import QQuickWindow
from PySide6.QtQuickControls2 import QQuickStyle
-import rc_window
+import rc_window # noqa: F401
# Append the parent directory of this file so that Python can find and
# import from the "shared" sibling directory.
sys.path.append(os.fspath(Path(__file__).parent.parent))
-from shared import shared_rc
+from shared import shared_rc # noqa: F401, E402
if __name__ == "__main__":
from PySide6.QtQuick3D import QQuick3D
# Imports to trigger the resources and registration of QML elements
-import resources_rc
-from examplepoint import ExamplePointGeometry
-from exampletriangle import ExampleTriangleGeometry
+import resources_rc # noqa: F401
+from examplepoint import ExamplePointGeometry # noqa: F401
+from exampletriangle import ExampleTriangleGeometry # noqa: F401
if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Basic"
);
}
- function generateTextureData() : ArrayBuffer {
+ function generateTextureData() {
let dataBuffer = new ArrayBuffer(width * height * 4)
let data = new Uint8Array(dataBuffer)
output.setBlueF(color1.blueF() + (value * (color2.blueF() - color1.blueF())))
return output
-
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
-from gradienttexture import GradientTexture
+from gradienttexture import GradientTexture # noqa: F401
+from pathlib import Path
+
+import os
import sys
if __name__ == "__main__":
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+ItemDelegate {
+ id: delegate
+
+ checkable: true
+
+ contentItem: ColumnLayout {
+ spacing: 10
+
+ Label {
+ text: fullName
+ font.bold: true
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+
+ GridLayout {
+ id: grid
+ visible: false
+
+ columns: 2
+ rowSpacing: 10
+ columnSpacing: 10
+
+ Label {
+ text: qsTr("Address:")
+ Layout.leftMargin: 60
+ }
+
+ Label {
+ text: address
+ font.bold: true
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("City:")
+ Layout.leftMargin: 60
+ }
+
+ Label {
+ text: city
+ font.bold: true
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Number:")
+ Layout.leftMargin: 60
+ }
+
+ Label {
+ text: number
+ font.bold: true
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: "expanded"
+ when: delegate.checked
+
+ PropertyChanges {
+ // TODO: When Qt Design Studio supports generalized grouped properties, change to:
+ // grid.visible: true
+ target: grid
+ visible: true
+ }
+ }
+ ]
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Dialog {
+ id: dialog
+
+ signal finished(string fullName, string address, string city, string number)
+
+ function createContact() {
+ form.fullName.clear();
+ form.address.clear();
+ form.city.clear();
+ form.number.clear();
+
+ dialog.title = qsTr("Add Contact");
+ dialog.open();
+ }
+
+ function editContact(contact) {
+ form.fullName.text = contact.fullName;
+ form.address.text = contact.address;
+ form.city.text = contact.city;
+ form.number.text = contact.number;
+
+ dialog.title = qsTr("Edit Contact");
+ dialog.open();
+ }
+
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+
+ focus: true
+ modal: true
+ title: qsTr("Add Contact")
+ standardButtons: Dialog.Ok | Dialog.Cancel
+
+ contentItem: ContactForm {
+ id: form
+ }
+
+ onAccepted: finished(form.fullName.text, form.address.text, form.city.text, form.number.text)
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+GridLayout {
+ id: grid
+ property alias fullName: fullName
+ property alias address: address
+ property alias city: city
+ property alias number: number
+ property int minimumInputSize: 120
+ property string placeholderText: qsTr("<enter>")
+
+ rows: 4
+ columns: 2
+
+ Label {
+ text: qsTr("Full Name")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: fullName
+ focus: true
+ Layout.fillWidth: true
+ Layout.minimumWidth: grid.minimumInputSize
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: grid.placeholderText
+ }
+
+ Label {
+ text: qsTr("Address")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: address
+ Layout.fillWidth: true
+ Layout.minimumWidth: grid.minimumInputSize
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: grid.placeholderText
+ }
+
+ Label {
+ text: qsTr("City")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: city
+ Layout.fillWidth: true
+ Layout.minimumWidth: grid.minimumInputSize
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: grid.placeholderText
+ }
+
+ Label {
+ text: qsTr("Number")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: number
+ Layout.fillWidth: true
+ Layout.minimumWidth: grid.minimumInputSize
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: grid.placeholderText
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+
+ property int currentContact: -1
+
+ width: 320
+ height: 480
+ visible: true
+ title: qsTr("Contact List")
+
+ ContactDialog {
+ id: contactDialog
+ onFinished: function(fullName, address, city, number) {
+ if (currentContact == -1)
+ contactView.model.append(fullName, address, city, number)
+ else
+ contactView.model.set(currentContact, fullName, address, city, number)
+ }
+ }
+
+ Menu {
+ id: contactMenu
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+ modal: true
+
+ Label {
+ padding: 10
+ font.bold: true
+ width: parent.width
+ horizontalAlignment: Qt.AlignHCenter
+ text: currentContact >= 0 ? contactView.model.get(currentContact).fullName : ""
+ }
+ MenuItem {
+ text: qsTr("Edit...")
+ onTriggered: contactDialog.editContact(contactView.model.get(currentContact))
+ }
+ MenuItem {
+ text: qsTr("Remove")
+ onTriggered: contactView.model.remove(currentContact)
+ }
+ }
+
+ ContactView {
+ id: contactView
+ anchors.fill: parent
+ onPressAndHold: {
+ currentContact = index
+ contactMenu.open()
+ }
+ }
+
+ RoundButton {
+ text: qsTr("+")
+ highlighted: true
+ anchors.margins: 10
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ onClicked: {
+ currentContact = -1
+ contactDialog.createContact()
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import Backend
+
+ListView {
+ id: listView
+
+ signal pressAndHold(int index)
+
+ width: 320
+ height: 480
+
+ focus: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ section.property: "fullName"
+ section.criteria: ViewSection.FirstCharacter
+ section.delegate: SectionDelegate {
+ width: listView.width
+ }
+
+ delegate: ContactDelegate {
+ id: delegate
+ width: listView.width
+ onPressAndHold: listView.pressAndHold(index)
+ }
+
+ model: ContactModel {
+ id: contactModel
+ }
+
+ ScrollBar.vertical: ScrollBar { }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ToolBar {
+ id: background
+
+ Label {
+ id: label
+ text: section
+ anchors.fill: parent
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ }
+}
--- /dev/null
+module Contact
+ContactList 1.0 ContactList.qml
+ContactDialog 1.0 ContactDialog.qml
+ContactDelegate 1.0 ContactDelegate.ui.qml
+ContactForm 1.0 ContactForm.ui.qml
+ContactView 1.0 ContactView.ui.qml
+SectionDelegate 1.0 SectionDelegate.ui.qml
--- /dev/null
+{
+ "files": ["main.py",
+ "contactmodel.py",
+ "Contact/ContactDialog.qml",
+ "Contact/ContactDelegate.ui.qml",
+ "Contact/ContactForm.ui.qml",
+ "Contact/ContactList.qml",
+ "Contact/ContactView.ui.qml",
+ "Contact/SectionDelegate.ui.qml"]
+}
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import bisect
+from dataclasses import dataclass
+from enum import IntEnum
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+ QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Backend"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class ContactModel(QAbstractListModel):
+
+ @QEnum
+ class ContactRole(IntEnum):
+ FullNameRole = Qt.DisplayRole
+ AddressRole = Qt.UserRole
+ CityRole = Qt.UserRole + 1
+ NumberRole = Qt.UserRole + 2
+
+ @dataclass
+ class Contact:
+ fullName: str
+ address: str
+ city: str
+ number: str
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.m_contacts = []
+ self.m_contacts.append(self.Contact("Angel Hogan", "Chapel St. 368 ", "Clearwater",
+ "0311 1823993"))
+ self.m_contacts.append(self.Contact("Felicia Patton", "Annadale Lane 2", "Knoxville",
+ "0368 1244494"))
+ self.m_contacts.append(self.Contact("Grant Crawford", "Windsor Drive 34", "Riverdale",
+ "0351 7826892"))
+ self.m_contacts.append(self.Contact("Gretchen Little", "Sunset Drive 348", "Virginia Beach",
+ "0343 1234991"))
+ self.m_contacts.append(self.Contact("Geoffrey Richards", "University Lane 54", "Trussville",
+ "0423 2144944"))
+ self.m_contacts.append(self.Contact("Henrietta Chavez", "Via Volto San Luca 3",
+ "Piobesi Torinese", "0399 2826994"))
+ self.m_contacts.append(self.Contact("Harvey Chandler", "North Squaw Creek 11",
+ "Madisonville", "0343 1244492"))
+ self.m_contacts.append(self.Contact("Miguel Gomez", "Wild Rose Street 13", "Trussville",
+ "0343 9826996"))
+ self.m_contacts.append(self.Contact("Norma Rodriguez", " Glen Eagles Street 53",
+ "Buffalo", "0241 5826596"))
+ self.m_contacts.append(self.Contact("Shelia Ramirez", "East Miller Ave 68", "Pickerington",
+ "0346 4844556"))
+ self.m_contacts.append(self.Contact("Stephanie Moss", "Piazza Trieste e Trento 77",
+ "Roata Chiusani", "0363 0510490"))
+
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.m_contacts)
+
+ def data(self, index: QModelIndex, role: int):
+ row = index.row()
+ if row < self.rowCount():
+ if role == ContactModel.ContactRole.FullNameRole:
+ return self.m_contacts[row].fullName
+ elif role == ContactModel.ContactRole.AddressRole:
+ return self.m_contacts[row].address
+ elif role == ContactModel.ContactRole.CityRole:
+ return self.m_contacts[row].city
+ elif role == ContactModel.ContactRole.NumberRole:
+ return self.m_contacts[row].number
+
+ def roleNames(self):
+ default = super().roleNames()
+ default[ContactModel.ContactRole.FullNameRole] = QByteArray(b"fullName")
+ default[ContactModel.ContactRole.AddressRole] = QByteArray(b"address")
+ default[ContactModel.ContactRole.CityRole] = QByteArray(b"city")
+ default[ContactModel.ContactRole.NumberRole] = QByteArray(b"number")
+ return default
+
+ @Slot(int)
+ def get(self, row: int):
+ contact = self.m_contacts[row]
+ return {"fullName": contact.fullName, "address": contact.address,
+ "city": contact.city, "number": contact.number}
+
+ @Slot(str, str, str, str)
+ def append(self, full_name: str, address: str, city: str, number: str):
+ contact = self.Contact(full_name, address, city, number)
+ contact_names = [contact.fullName for contact in self.m_contacts]
+ index = bisect.bisect(contact_names, contact.fullName)
+ self.beginInsertRows(QModelIndex(), index, index)
+ self.m_contacts.insert(index, contact)
+ self.endInsertRows()
+
+ @Slot(int, str, str, str, str)
+ def set(self, row: int, full_name: str, address: str, city: str, number: str):
+ if row < 0 or row >= len(self.m_contacts):
+ return
+
+ self.m_contacts[row] = self.Contact(full_name, address, city, number)
+ self.dataChanged(self.index(row, 0), self.index(row, 0),
+ [ContactModel.ContactRole.FullNameRole,
+ ContactModel.ContactRole.AddressRole,
+ ContactModel.ContactRole.CityRole,
+ ContactModel.ContactRole.NumberRole])
+
+ @Slot(int)
+ def remove(self, row):
+ if row < 0 or row >= len(self.m_contacts):
+ return
+
+ self.beginRemoveRows(QModelIndex(), row, row)
+ del self.m_contacts[row]
+ self.endRemoveRows()
--- /dev/null
+Qt Quick Controls - Contact List
+================================
+
+.. tags:: Android
+
+A QML app using Qt Quick Controls and a Python class that implements a simple
+contact list. This example can also be deployed to Android using
+**pyside6-android-deploy**
+
+A PySide6 application that demonstrates the analogous example in Qt
+`ContactsList <https://doc.qt.io/qt-6.6/qtquickcontrols-contactlist-example.html>`_
+
+.. image:: qtquickcontrols-contactlist.png
+ :width: 400
+ :alt: ContactList Screenshot
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""
+PySide6 port of Qt Quick Controls Contact List example from Qt v6.x
+"""
+import sys
+from pathlib import Path
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from contactmodel import ContactModel # noqa: F401
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ app.setOrganizationName("QtProject")
+ app.setApplicationName("ContactsList")
+ engine = QQmlApplicationEngine()
+
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("Contact", "ContactList")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ del engine
+ sys.exit(app.exec())
file = QFile(path)
mime = self.db.mimeTypeForFile(QFileInfo(file))
- if 'text' in mime.comment().lower() or any('text' in s.lower() for s in mime.parentMimeTypes()):
+ if ('text' in mime.comment().lower()
+ or any('text' in s.lower() for s in mime.parentMimeTypes())):
if file.open(QFile.ReadOnly | QFile.Text):
stream = QTextStream(file).readAll()
return stream
Qt Quick Controls 2 - Gallery
=============================
+.. tags:: Android
+
The gallery example is a simple application with a drawer menu that contains
all the Qt Quick Controls 2. Each menu item opens a page that shows the
graphical appearance of a control, allows you to interact with the control, and
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuickControls2 import QQuickStyle
-import rc_gallery
+import rc_gallery # noqa: F401
if __name__ == "__main__":
QGuiApplication.setApplicationName("Gallery")
w = MainWindow()
w.show()
sys.exit(QCoreApplication.exec())
-
self.m_ui.actionDisconnect.triggered.connect(self.disconnect_device)
self.m_ui.actionResetController.triggered.connect(self._reset_controller)
self.m_ui.actionQuit.triggered.connect(self.close)
- self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)
+ self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt) # noqa: F821
self.m_ui.actionClearLog.triggered.connect(self.m_model.clear)
self.m_ui.actionPluginDocumentation.triggered.connect(show_help)
self.m_ui.actionDeviceInformation.triggered.connect(self._action_device_information)
else:
self.m_model.set_queue_limit(0)
- device, error_string = QCanBus.instance().createDevice(p.plugin_name, p.device_interface_name)
+ device, error_string = QCanBus.instance().createDevice(
+ p.plugin_name, p.device_interface_name)
if not device:
- self.m_status.setText(f"Error creating device '{p.plugin_name}', reason: '{error_string}'")
+ self.m_status.setText(
+ f"Error creating device '{p.plugin_name}', reason: '{error_string}'")
return
self.m_number_frames_written = 0
config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.BitRateKey)
if config_bit_rate > 0:
is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.CanFdKey))
- config_data_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.DataBitRateKey)
+ config_data_bit_rate = self.m_can_device.configurationParameter(
+ QCanBusDevice.DataBitRateKey)
bit_rate = config_bit_rate / 1000
if is_can_fd and config_data_bit_rate > 0:
data_bit_rate = config_data_bit_rate / 1000
- m = f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} at {bit_rate} / {data_bit_rate} kBit/s"
+ m = (f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} "
+ f"at {bit_rate} / {data_bit_rate} kBit/s")
self.m_status.setText(m)
else:
- m = f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} at {bit_rate} kBit/s"
+ m = (f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} "
+ f"at {bit_rate} kBit/s")
self.m_status.setText(m)
else:
- self.m_status.setText(f"Plugin: {p.plugin_name}, connected to {p.device_interface_name}")
+ self.m_status.setText(
+ f"Plugin: {p.plugin_name}, connected to {p.device_interface_name}")
if self.m_can_device.hasBusStatus():
self.m_busStatusTimer.start(2000)
@Slot(bool)
def _flexible_datarate(self, value):
- l = MAX_PAYLOAD_FD if value else MAX_PAYLOAD
- self.m_hexStringValidator.set_max_length(l)
+ len = MAX_PAYLOAD_FD if value else MAX_PAYLOAD
+ self.m_hexStringValidator.set_max_length(len)
self.m_ui.bitrateSwitchBox.setEnabled(value)
if not value:
self.m_ui.bitrateSwitchBox.setChecked(False)
# do not go beyond 10 entries
number_of_entries = min(int(self.ui.readSize.currentText()),
- 10 - start_address)
+ 10 - start_address)
return QModbusDataUnit(table, start_address, number_of_entries)
def write_request(self):
# do not go beyond 10 entries
number_of_entries = min(int(self.ui.writeSize.currentText()),
- 10 - start_address)
+ 10 - start_address)
return QModbusDataUnit(table, start_address, number_of_entries)
self.m_ui.actionConfigure.triggered.connect(self.m_settings.show)
self.m_ui.actionClear.triggered.connect(self.m_console.clear)
self.m_ui.actionAbout.triggered.connect(self.about)
- self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)
+ self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt) # noqa: F821
self.m_serial.errorOccurred.connect(self.handle_error)
self.m_serial.readyRead.connect(self.read_data)
self.string_data_bits = ""
self.parity = QSerialPort.NoParity
self.string_parity = ""
- self.stop_bits = QSerialPort.OneStop
+ self.stop_bits = QSerialPort.OneStop
self.string_stop_bits = ""
self.flow_control = QSerialPort.SoftwareControl
self.string_flow_control = ""
self.m_ui.applyButton.clicked.connect(self.apply)
self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.show_port_info)
self.m_ui.baudRateBox.currentIndexChanged.connect(self.check_custom_baud_rate_policy)
- self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.check_custom_device_path_policy)
+ self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(
+ self.check_custom_device_path_policy)
self.fill_ports_parameters()
self.fill_ports_info()
self._file_dialog = QFileDialog(self, "Open Audio File", directory)
self._file_dialog.setAcceptMode(QFileDialog.AcceptOpen)
mime_types = ["audio/mpeg", "audio/aac", "audio/x-ms-wma",
- "audio/x-flac+ogg", "audio/x-wav"]
+ "audio/x-flac+ogg", "audio/x-wav"]
self._file_dialog.setMimeTypeFilters(mime_types)
self._file_dialog.selectMimeTypeFilter(mime_types[0])
def __init__(self, parent=None):
QSqlRelationalDelegate.__init__(self, parent)
- self.star = QPixmap(":/images/star.png")
+ self.star = QPixmap(":/images/star.svg")
+ self.star_filled = QPixmap(":/images/star-filled.svg")
def paint(self, painter, option, index):
""" Paint the items in the table.
if option.state & QStyle.State_Selected:
painter.fillRect(option.rect,
- option.palette.color(color_group, QPalette.Highlight))
+ option.palette.color(color_group, QPalette.Highlight))
rating = model.data(index, Qt.DisplayRole)
width = self.star.width()
height = self.star.height()
x = option.rect.x()
y = option.rect.y() + (option.rect.height() / 2) - (height / 2)
- for i in range(rating):
- painter.drawPixmap(x, y, self.star)
+ for i in range(5):
+ if i < rating:
+ painter.drawPixmap(x, y, self.star_filled)
+ else:
+ painter.drawPixmap(x, y, self.star)
x += width
-
pen = painter.pen()
painter.setPen(option.palette.color(QPalette.Mid))
painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
- <file>images/star.png</file>
+ <file>images/star.svg</file>
+ <file>images/star-filled.svg</file>
</qresource>
</RCC>
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
- QHeaderView, QMainWindow, QMessageBox)
+ QHeaderView, QMainWindow, QMessageBox)
from PySide6.QtGui import QKeySequence
from PySide6.QtSql import QSqlRelation, QSqlRelationalTableModel, QSqlTableModel
from PySide6.QtCore import Qt, Slot
# Lock and prohibit resizing of the width of the rating column:
self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"),
- QHeaderView.ResizeToContents)
+ QHeaderView.ResizeToContents)
mapper = QDataWidgetMapper(self)
mapper.setModel(model)
def showError(self, err):
QMessageBox.critical(self, "Unable to initialize Database",
- f"Error initializing database: {err.text()}")
+ f"Error initializing database: {err.text()}")
def create_menubar(self):
file_menu = self.menuBar().addMenu(self.tr("&File"))
quit_action = file_menu.addAction(self.tr("&Quit"))
- quit_action.triggered.connect(qApp.quit)
+ quit_action.triggered.connect(qApp.quit) # noqa: F821
help_menu = self.menuBar().addMenu(self.tr("&Help"))
about_action = help_menu.addAction(self.tr("&About"))
about_action.setShortcut(QKeySequence.HelpContents)
about_action.triggered.connect(self.about)
aboutQt_action = help_menu.addAction("&About Qt")
- aboutQt_action.triggered.connect(qApp.aboutQt)
+ aboutQt_action.triggered.connect(qApp.aboutQt) # noqa: F821
@Slot()
def about(self):
QMessageBox.about(self, self.tr("About Books"),
- self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
- "with a model/view framework."))
+ self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
+ "with a model/view framework."))
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#0d0d0d"><path d="M8.41 18.138L12 15.845l3.59 2.323-.94-4.345 3.162-2.897-4.159-.392L12 6.43l-1.652 4.073-4.159.392 3.162 2.927-.94 4.315zm-1.346 3.696a1.04 1.04 0 0 1-1.567-1.104l1.318-6.033-4.476-4.11c-.665-.611-.293-1.726.604-1.808l5.866-.539 2.229-5.587c.348-.872 1.575-.872 1.923 0l2.229 5.587 5.866.539c.897.082 1.269 1.197.604 1.808l-4.476 4.11 1.318 6.033a1.04 1.04 0 0 1-1.567 1.104L12 18.681l-4.935 3.153z"/><path d="M12 5l-1.796 5.528H4.392l4.702 3.416-1.796 5.528L12 16.056l4.702 3.416-1.796-5.528 4.702-3.416h-5.812L12 5z"/></svg>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path d="M8.41 18.138L12 15.845l3.59 2.323-.94-4.345 3.162-2.897-4.159-.392L12 6.43l-1.652 4.073-4.159.392 3.162 2.927-.94 4.315zm-1.346 3.696a1.04 1.04 0 0 1-1.567-1.104l1.318-6.033-4.476-4.11c-.665-.611-.293-1.726.604-1.808l5.866-.539 2.229-5.587c.348-.872 1.575-.872 1.923 0l2.229 5.587 5.866.539c.897.082 1.269 1.197.604 1.808l-4.476 4.11 1.318 6.033a1.04 1.04 0 0 1-1.567 1.104L12 18.681l-4.935 3.153z" fill="#0d0d0d"/></svg>
import sys
from PySide6.QtWidgets import QApplication
from bookwindow import BookWindow
-import rc_books
+import rc_books # noqa: F401
if __name__ == "__main__":
app = QApplication([])
# Resource object code (Python 3)
# Created by: object code
-# Created by: The Resource Compiler for Qt version 6.2.2
+# Created by: The Resource Compiler for Qt version 6.6.1
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
qt_resource_data = b"\
-\x00\x00\x03\x0e\
-\x89\
-PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
-\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\
-\x00\x00\x00\x09pHYs\x00\x00\x0b\x11\x00\x00\x0b\x11\
-\x01\x7fd_\x91\x00\x00\x00\x07tIME\x07\xd4\x09\
-\x03\x12\x11\x08\x18~\xe5:\x00\x00\x00\x06bKGD\
-\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x02\x9bID\
-AT8\xcbc\x98:c\x1e#:\xe6\xe5d\xcf\x17\
-\x12\x12\x16\xc4&\x87\x8e\x19\xb0\x09v\xc6\x18\xb7x\xea\
-\x8b\xcd\x9c=o\x09i\x06,X4\x8f\xf1\xd2\xa5\x99\
-L\xb9\xa1\x16\xc5\xc7\xbb\xed\xff\x0a\xf2\xb2;M\x9f\xb5\
-\x908\x03\x16,\x9a\xcb\xf8\xe0\xde\x04\x96\xc7\x0f\xdby\
-\xe7MO\xc8\xfbv\xbf\xe5\xff\xb4\x0a\x9b\x9by\x851\
-\xdc\xd3g-\x82k\x983\x7f)\xe3l F1`\
-\xca\xf4y\x8c\xd7\xaeMg\x02i~\xf2\xa8Y\xe1\xd2\
-\xa5\xfa\xdc_\x9f7\xfd\xffx\xbf\xea\x7fE\x96m\x97\
-\x81\x81>'33\x8b\xa5\x9e8gi\xb8\x9e\xc0f\
-&&\xa6D\x14\x03&N\x9d\xc7x\xef\xdeD\x96'\
-\x0f[E\x9f>j\xd6\xbdu\xb3\x22\xef\xd7\xb7=\xff\
-\xbe\x7f\xe8\xfb\x7f~S\xcc\xef\x05\xc5\xea\x9fNOQ\
-\xfb\x7f\xbaM\xed\xbf\x87\x1a\xefn5-\x1dV\x14\x03\
-f\xcf[\xce\xa8\xa4\xa9![W\xed\x9b}\xefJ\xcb\
-\xcew\xaf&\x7f\xfa\xfee\xc9\xff\xef\x1f\xfa\xff\xbf\xbf\
-\x95\xf2\xff\xc9^\x83\xffW\x17\xaa\xfdot\x12{\xc4\
-\xc7\xc7/\x8e\x12\x06Y\xb9\x85\xcc\xb2\x82\x1c\xf3\xa7D\
-\xab\xfe\xfa\xbe%\xe2\xff\x8fgm\xff\x7f|\x9a\x08\xd6\
-\xfc\xf5Y\xcd\xff\xd7\xe7\xfc\xfe\xdf\xde\xa0\xf5\x7fE\x94\
-\xecO\x16\x16V\xebi3\xe7\xa3\x06\xe2\xe4is\x18\
-\xe7/Z\xc1\xc8\xce\xc1i\x10\xe5\xa8\xd2\xbe\xa6\xcd\xe7\
-\xf6\xc3m\x99\xff~^(\xf8\xff\xe1j\xe0\xff\x17G\
-L\xff\xdf\xdf\xae\xf6\xbf\xc2]\xf4\xba\x9a\x9a\x06\x1bF\
-,\x00\x01#2\xe6\xe6\xe6`Q\xd6\x941_\xde\xe4\
-q\xfb\xc3y\xd3\xff\x1b\x8aT\xff\xbf?`\xff\xff\xdc\
-l\xe5\xff\xea\xc2\x1c9\xd3g-\xc0i\x00\x13\x10\x8b\
-\x03\xb1?\x10\xe7\xf5\x16\xd8\xde\xf8p\xc6\xe4\xbf\x9d<\
-\xf7t\x7fC\xe9\x95\xb7\x96\xd9\xff\x9b\x9c,\xfdN@\
-@H\x14\x9b\x01LP,\x06\xc4\x19@|\x22;\xca\
-\xf0\xe7\xe9\xf9\x06\xff\x81\xec\x03@\xbc^\x82\x9f\xf3\xf6\
-\x9e\x1a\xf3_az\x823P\xd2\x01T#3\x10\x0b\
-\x00\xb1\x1e\x10\x17\x03\xf1\xd1\xa8@\xdd\x9f\xad\x09J \
-\x03\xfe\x00\xf17 >\x0f\xb4kf\xb9\xa7\xea\x0d}\
-i>#d\x03\xb4\x808\x08\x88k\x81x\x09\xd4\xc6\
-\x1b\x11a\x06\xdf\xec\x94\xb8\xdf\x03\xd9;\x81x\x1a\x10\
-\xf7\x82\xd4\xb0\xb2\xb1G\xf9\xda\x99:L\x9d9\x9f\x09\
-f\x80\x0e\x10;\x02\xb1\x13\x10[\x00\xb1\x01\x10\x07\x06\
-{h\x9c\x02\xd2k\xa0\x86\x8b\x001\x17\x10\xf3\x80\xb0\
-\x88\x88(\xcb,hFC\xf6\x02\x08\xb3\x001+\x10\
-K122\xe4\x01i7 \x96\x01b6\xa88\x0b\
-T=cW\xef$\xb0\x01\x00\xceo{\xf5UL\xf0\
-\xac\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x02e\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2224\
+\x22 height=\x2224\x22 fi\
+ll=\x22#0d0d0d\x22><pa\
+th d=\x22M8.41 18.1\
+38L12 15.845l3.5\
+9 2.323-.94-4.34\
+5 3.162-2.897-4.\
+159-.392L12 6.43\
+l-1.652 4.073-4.\
+159.392 3.162 2.\
+927-.94 4.315zm-\
+1.346 3.696a1.04\
+ 1.04 0 0 1-1.56\
+7-1.104l1.318-6.\
+033-4.476-4.11c-\
+.665-.611-.293-1\
+.726.604-1.808l5\
+.866-.539 2.229-\
+5.587c.348-.872 \
+1.575-.872 1.923\
+ 0l2.229 5.587 5\
+.866.539c.897.08\
+2 1.269 1.197.60\
+4 1.808l-4.476 4\
+.11 1.318 6.033a\
+1.04 1.04 0 0 1-\
+1.567 1.104L12 1\
+8.681l-4.935 3.1\
+53z\x22/><path d=\x22M\
+12 5l-1.796 5.52\
+8H4.392l4.702 3.\
+416-1.796 5.528L\
+12 16.056l4.702 \
+3.416-1.796-5.52\
+8 4.702-3.416h-5\
+.812L12 5z\x22/></s\
+vg>\x0a\
+\x00\x00\x01\xfa\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2224\
+\x22 height=\x2224\x22 fi\
+ll=\x22none\x22><path \
+d=\x22M8.41 18.138L\
+12 15.845l3.59 2\
+.323-.94-4.345 3\
+.162-2.897-4.159\
+-.392L12 6.43l-1\
+.652 4.073-4.159\
+.392 3.162 2.927\
+-.94 4.315zm-1.3\
+46 3.696a1.04 1.\
+04 0 0 1-1.567-1\
+.104l1.318-6.033\
+-4.476-4.11c-.66\
+5-.611-.293-1.72\
+6.604-1.808l5.86\
+6-.539 2.229-5.5\
+87c.348-.872 1.5\
+75-.872 1.923 0l\
+2.229 5.587 5.86\
+6.539c.897.082 1\
+.269 1.197.604 1\
+.808l-4.476 4.11\
+ 1.318 6.033a1.0\
+4 1.04 0 0 1-1.5\
+67 1.104L12 18.6\
+81l-4.935 3.153z\
+\x22 fill=\x22#0d0d0d\x22\
+/></svg>\x0a\
"
qt_resource_name = b"\
\x07\x03}\xc3\
\x00i\
\x00m\x00a\x00g\x00e\x00s\
+\x00\x0f\
+\x02\x11 \x07\
+\x00s\
+\x00t\x00a\x00r\x00-\x00f\x00i\x00l\x00l\x00e\x00d\x00.\x00s\x00v\x00g\
\x00\x08\
-\x0a\x85X\x07\
+\x0a\x85U\x87\
\x00s\
-\x00t\x00a\x00r\x00.\x00p\x00n\x00g\
+\x00t\x00a\x00r\x00.\x00s\x00v\x00g\
"
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01z\xe7\xee&\xfd\
+\x00\x00\x01\x8c\xd4\xc79\xcf\
+\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x02i\
+\x00\x00\x01\x8c\xd4\xc79\xcf\
"
def qInitResources():
self.window = window
def eventTest(self, event):
- if (event.type() == QEvent.StateMachineWrapped and
- event.event().type() == QEvent.KeyPress):
+ if (event.type() == QEvent.StateMachineWrapped
+ and event.event().type() == QEvent.KeyPress):
key = event.event().key()
- return (key == Qt.Key_2 or key == Qt.Key_8 or
- key == Qt.Key_6 or key == Qt.Key_4)
+ return (key == Qt.Key_2 or key == Qt.Key_8
+ or key == Qt.Key_6 or key == Qt.Key_4)
return False
def onTransition(self, event):
for x in range(self.width):
column = []
for y in range(self.height):
- if (x == 0 or x == self.width - 1 or y == 0 or
- y == self.height - 1 or generator.bounded(0, 40) == 0):
+ if (x == 0 or x == self.width - 1 or y == 0
+ or y == self.height - 1 or generator.bounded(0, 40) == 0):
column.append('#')
else:
column.append('.')
input_state.addTransition(quit_transition)
machine.setInitialState(input_state)
- machine.finished.connect(qApp.quit)
+ machine.finished.connect(qApp.quit) # noqa: F821
machine.start()
def sizeHint(self):
options.append(("--pyside-shared-libraries-qmake",
lambda: get_shared_libraries_qmake(Package.PYSIDE_MODULE), pyside_libs_error,
- "Print paths of f{PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) for qmake"))
+ "Print paths of f{PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) "
+ "for qmake"))
options.append(("--pyside-shared-libraries-cmake",
lambda: get_shared_libraries_cmake(Package.PYSIDE_MODULE), pyside_libs_error,
- f"Print paths of {PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) for cmake"))
+ f"Print paths of {PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) "
+ "for cmake"))
options_usage = ''
for i, (flag, _, _, description) in enumerate(options):
# libraries when compiling the project
baseName = os.path.basename(lib)
link = ' -l'
- if sys.platform in ['linux', 'linux2']: # Linux: 'libfoo.so' -> '/absolute/path/libfoo.so'
+ if sys.platform in ['linux', 'linux2']: # Linux: 'libfoo.so' -> '/absolute/path/libfoo.so'
link = lib
- elif sys.platform in ['darwin']: # Darwin: 'libfoo.so' -> '-lfoo'
+ elif sys.platform in ['darwin']: # Darwin: 'libfoo.so' -> '-lfoo'
link += os.path.splitext(baseName[3:])[0]
- else: # Windows: 'libfoo.dll' -> 'libfoo.dll'
+ else: # Windows: 'libfoo.dll' -> 'libfoo.dll'
link += os.path.splitext(baseName)[0]
return link
# e.g.: "Program Files" to "Progra~1"
for d in libdir.split("\\"):
if " " in d:
- libdir = libdir.replace(d, d.split(" ")[0][:-1]+"~1")
+ libdir = libdir.replace(d, d.split(" ")[0][:-1] + "~1")
lib_flags = flags['lib']
return f'-L{libdir} -l{lib_flags}'
elif sys.platform == 'darwin':
from PySide6.QtGui import QGuiApplication
from PySide6.QtWebEngineQuick import QtWebEngineQuick
-import rc_resources
+import rc_resources # noqa: F401
# To be used on the @QmlElement decorator
if __name__ == '__main__':
- QCoreApplication.setApplicationName("Quick Nano Browser");
- QCoreApplication.setOrganizationName("QtProject");
+ QCoreApplication.setApplicationName("Quick Nano Browser")
+ QCoreApplication.setOrganizationName("QtProject")
QtWebEngineQuick.initialize()
from PySide6.QtWidgets import QApplication
from mainwindow import MainWindow
-import rc_markdowneditor
+import rc_markdowneditor # noqa: F401
if __name__ == '__main__':
self.notification.closed.connect(self.onClosed)
QTimer.singleShot(10000, lambda: self.onClosed())
- self.move(self.parentWidget().mapToGlobal(self.parentWidget().rect().bottomRight() -
- QPoint(self.width() + 10, self.height() + 10)))
+ self.move(self.parentWidget().mapToGlobal(self.parentWidget().rect().bottomRight()
+ - QPoint(self.width() + 10, self.height() + 10)))
@Slot()
def onClosed(self):
s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
s.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
s.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, False)
- self._profile.downloadRequested.connect(self._download_manager_widget.download_requested)
+ self._profile.downloadRequested.connect(
+ self._download_manager_widget.download_requested)
profile = QWebEngineProfile.defaultProfile() if offTheRecord else self._profile
main_window = BrowserWindow(self, profile, False)
if not forDevTools:
self._tab_widget.link_hovered.connect(self._show_status_message)
self._tab_widget.load_progress.connect(self.handle_web_view_load_progress)
- self._tab_widget.web_action_enabled_changed.connect(self.handle_web_action_enabled_changed)
+ self._tab_widget.web_action_enabled_changed.connect(
+ self.handle_web_action_enabled_changed)
self._tab_widget.url_changed.connect(self._url_changed)
self._tab_widget.fav_icon_changed.connect(self._fav_action.setIcon)
self._tab_widget.dev_tools_requested.connect(self.handle_dev_tools_requested)
def create_help_menu(self):
help_menu = QMenu("Help")
- help_menu.addAction("About Qt", qApp.aboutQt)
+ help_menu.addAction("About Qt", qApp.aboutQt) # noqa: F821
return help_menu
@Slot()
class DownloadWidget(QFrame):
"""Displays one ongoing or finished download (QWebEngineDownloadRequest)."""
-
# This signal is emitted when the user indicates that they want to remove
# this download from the downloads list.
remove_clicked = Signal(QWidget)
from browser import Browser
-import data.rc_simplebrowser
+import data.rc_simplebrowser # noqa: F401
if __name__ == "__main__":
parser = ArgumentParser(description="Qt Widgets Web Browser",
old_page.createCertificateErrorDialog.disconnect(self.handle_certificate_error)
old_page.authenticationRequired.disconnect(self.handle_authentication_required)
old_page.featurePermissionRequested.disconnect(self.handle_feature_permission_requested)
- old_page.proxyAuthenticationRequired.disconnect(self.handle_proxy_authentication_required)
- old_page.registerProtocolHandlerRequested.disconnect(self.handle_register_protocol_handler_requested)
+ old_page.proxyAuthenticationRequired.disconnect(
+ self.handle_proxy_authentication_required)
+ old_page.registerProtocolHandlerRequested.disconnect(
+ self.handle_register_protocol_handler_requested)
old_page.fileSystemAccessRequested.disconnect(self.handle_file_system_access_requested)
self.create_web_action_trigger(page, QWebEnginePage.Forward)
page.authenticationRequired.connect(self.handle_authentication_required)
page.featurePermissionRequested.connect(self.handle_feature_permission_requested)
page.proxyAuthenticationRequired.connect(self.handle_proxy_authentication_required)
- page.registerProtocolHandlerRequested.connect(self.handle_register_protocol_handler_requested)
+ page.registerProtocolHandlerRequested.connect(
+ self.handle_register_protocol_handler_requested)
page.fileSystemAccessRequested.connect(self.handle_file_system_access_requested)
def load_progress(self):
question = question_for_feature(feature).replace("%1", host)
w = self.window()
page = self.page()
- if (question
- and QMessageBox.question(w, title, question) == QMessageBox.Yes):
+ if question and QMessageBox.question(w, title, question) == QMessageBox.Yes:
page.setFeaturePermission(securityOrigin, feature,
QWebEnginePage.PermissionGrantedByUser)
else:
from PySide6.QtCore import QUrl, Slot
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import (QApplication, QLineEdit,
- QMainWindow, QPushButton, QToolBar)
+ QMainWindow, QPushButton, QToolBar)
from PySide6.QtWebEngineCore import QWebEnginePage
from PySide6.QtWebEngineWidgets import QWebEngineView
QGraphicsWidget, QStyle)
from PySide6.QtStateMachine import QState, QStateMachine
-import animatedtiles_rc
+import animatedtiles_rc # noqa: F401
# Deriving from more than one wrapped class is not supported, so we use
painter.drawEllipse(r.adjusted(5, 5, -5, -5))
painter.drawPixmap(-self._pix.width() / 2, -self._pix.height() / 2,
- self._pix)
+ self._pix)
def mousePressEvent(self, ev):
self.pressed.emit()
for i in range(64):
item = Pixmap(kinetic_pix)
item.pixmap_item.setOffset(-kinetic_pix.width() / 2,
- -kinetic_pix.height() / 2)
+ -kinetic_pix.height() / 2)
item.pixmap_item.setZValue(i)
items.append(item)
scene.addItem(item.pixmap_item)
for i, item in enumerate(items):
# Ellipse.
ellipse_state.assignProperty(item, 'pos',
- QPointF(math.cos((i / 63.0) * 6.28) * 250,
- math.sin((i / 63.0) * 6.28) * 250))
+ QPointF(math.cos((i / 63.0) * 6.28) * 250,
+ math.sin((i / 63.0) * 6.28) * 250))
# Figure 8.
figure_8state.assignProperty(item, 'pos',
- QPointF(math.sin((i / 63.0) * 6.28) * 250,
- math.sin(((i * 2) / 63.0) * 6.28) * 250))
+ QPointF(math.sin((i / 63.0) * 6.28) * 250,
+ math.sin(((i * 2) / 63.0) * 6.28) * 250))
# Random.
random_state.assignProperty(item, 'pos',
- QPointF(-250 + generator.bounded(0, 500),
- -250 + generator.bounded(0, 500)))
+ QPointF(-250 + generator.bounded(0, 500),
+ -250 + generator.bounded(0, 500)))
# Tiled.
+ width = kinetic_pix.width()
+ height = kinetic_pix.height()
tiled_state.assignProperty(item, 'pos',
- QPointF(((i % 8) - 4) * kinetic_pix.width() + kinetic_pix.width() / 2,
- ((i // 8) - 4) * kinetic_pix.height() + kinetic_pix.height() / 2))
+ QPointF(((i % 8) - 4) * width + width / 2,
+ ((i // 8) - 4) * height + height / 2))
# Centered.
centered_state.assignProperty(item, 'pos', QPointF())
view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
view.setBackgroundBrush(QBrush(bg_pix))
view.setCacheMode(QGraphicsView.CacheBackground)
- view.setRenderHints(
- QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
+ view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
view.show()
states = QStateMachine()
QGraphicsWidget)
from PySide6.QtStateMachine import QState, QStateMachine
-import appchooser_rc
+import appchooser_rc # noqa: F401
class Pixmap(QGraphicsWidget):
curve_types = [(f"QEasingCurve.{e.name}", e) for e in QEasingCurve.Type if e.value <= 40]
-
with QPainter(pix) as painter:
for curve_name, curve_type in curve_types:
# Start point.
painter.setBrush(Qt.red)
- start = QPoint(y_axis,
- x_axis - curve_scale * curve.valueForProgress(0))
+ start = QPoint(y_axis, x_axis - curve_scale * curve.valueForProgress(0))
painter.drawRect(start.x() - 1, start.y() - 1, 3, 3)
# End point.
painter.setBrush(Qt.blue)
end = QPoint(y_axis + curve_scale,
- x_axis - curve_scale * curve.valueForProgress(1))
+ x_axis - curve_scale * curve.valueForProgress(1))
painter.drawRect(end.x() - 1, end.y() - 1, 3, 3)
curve_path = QPainterPath()
t = 0.0
while t <= 1.0:
to = QPointF(y_axis + curve_scale * t,
- x_axis - curve_scale * curve.valueForProgress(t))
+ x_axis - curve_scale * curve.valueForProgress(t))
curve_path.lineTo(to)
t += 1.0 / curve_scale
self._anim.setCurrentTime(0)
is_elastic = (curve_type.value >= QEasingCurve.InElastic.value
- and curve_type.value <= QEasingCurve.OutInElastic.value)
+ and curve_type.value <= QEasingCurve.OutInElastic.value)
is_bounce = (curve_type.value >= QEasingCurve.InBounce.value
- and curve_type.value <= QEasingCurve.OutInBounce.value)
+ and curve_type.value <= QEasingCurve.OutInBounce.value)
self._ui.periodSpinBox.setEnabled(is_elastic)
self._ui.amplitudeSpinBox.setEnabled(is_elastic or is_bounce)
self._ui.overshootSpinBox.setEnabled(curve_type.value >= QEasingCurve.InBack.value
- and curve_type.value <= QEasingCurve.OutInBack.value)
+ and curve_type.value <= QEasingCurve.OutInBack.value)
def path_changed(self, index):
self._anim.set_path_type(index)
from PySide6.QtStateMachine import QState, QStateMachine
-import states_rc
+import states_rc # noqa: F401
class Pixmap(QGraphicsObject):
QSpinBox, QStyle, QSystemTrayIcon, QTextEdit,
QVBoxLayout)
-import rc_systray
+import rc_systray # noqa: F401
class Window(QDialog):
self._body_label = QLabel("Body:")
self._body_edit = QTextEdit()
- self._body_edit.setPlainText("Don't believe me. Honestly, I don't have a clue."
- "\nClick this balloon for details.")
+ self._body_edit.setPlainText("Don't believe me. Honestly, I don't have a clue.\n"
+ "Click this balloon for details.")
self._show_message_button = QPushButton("Show Message")
self._show_message_button.setDefault(True)
self._restore_action.triggered.connect(self.showNormal)
self._quit_action = QAction("Quit", self)
- self._quit_action.triggered.connect(qApp.quit)
+ self._quit_action.triggered.connect(qApp.quit) # noqa: F821
def create_tray_icon(self):
self._tray_icon_menu = QMenu(self)
from listchooser import PropertyChooser, SignalChooser
-import classwizard_rc
+import classwizard_rc # noqa: F401
BASE_CLASSES = ['<None>', 'PySide6.QtCore.QObject',
def _choose_output_dir(self):
directory = QFileDialog.getExistingDirectory(self, "Output Directory",
- self.output_dir())
+ self.output_dir())
if directory:
self.set_output_dir(directory)
self._lineedit = QLineEdit()
self._lineedit.setClearButtonEnabled(True)
re = QRegularExpression(pattern)
- assert(re.isValid())
+ assert re.isValid()
self._validator = QRegularExpressionValidator(re, self)
self._lineedit.setValidator(self._validator)
self._form_layout.addRow(label, self._lineedit)
@Slot()
def set_integer(self):
i, ok = QInputDialog.getInt(self,
- "QInputDialog.getInteger()", "Percentage:", 25, 0, 100, 1)
+ "QInputDialog.getInteger()", "Percentage:", 25, 0, 100, 1)
if ok:
self._integer_label.setText(f"{i}%")
@Slot()
def set_double(self):
d, ok = QInputDialog.getDouble(self, "QInputDialog.getDouble()",
- "Amount:", 37.56, -10000, 10000, 2)
+ "Amount:", 37.56, -10000, 10000, 2)
if ok:
self._double_label.setText(f"${d:g}")
def set_item(self):
items = ("Spring", "Summer", "Fall", "Winter")
- item, ok = QInputDialog.getItem(self, "QInputDialog.getItem()",
- "Season:", items, 0, False)
+ item, ok = QInputDialog.getItem(self, "QInputDialog.getItem()", "Season:", items, 0, False)
if ok and item:
self._item_label.setText(item)
@Slot()
def set_text(self):
text, ok = QInputDialog.getText(self, "QInputDialog.getText()",
- "User name:", QLineEdit.Normal,
- QDir.home().dirName())
+ "User name:", QLineEdit.Normal, QDir.home().dirName())
if ok and text != '':
self._text_label.setText(text)
@Slot()
def set_multiline_text(self):
text, ok = QInputDialog.getMultiLineText(self, "QInputDialog::getMultiLineText()",
- "Address:", "John Doe\nFreedom Street")
+ "Address:", "John Doe\nFreedom Street")
if ok and text != '':
self._multiline_text_label.setText(text)
options_value = self._file_options.value()
options = QFileDialog.Options(options_value) | QFileDialog.ShowDirsOnly
- directory = QFileDialog.getExistingDirectory(self,
- "QFileDialog.getExistingDirectory()",
- self._directory_label.text(), options)
+ directory = QFileDialog.getExistingDirectory(self, "QFileDialog.getExistingDirectory()",
+ self._directory_label.text(), options)
if directory:
self._directory_label.setText(directory)
options_value = self._file_options.value()
options = QFileDialog.Options(options_value)
- fileName, filtr = QFileDialog.getOpenFileName(self,
- "QFileDialog.getOpenFileName()",
- self._open_file_name_label.text(),
- "All Files (*);;Text Files (*.txt)", "", options)
+ fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()",
+ self._open_file_name_label.text(),
+ "All Files (*);;Text Files (*.txt)", "", options)
if fileName:
self._open_file_name_label.setText(fileName)
options_value = self._file_options.value()
options = QFileDialog.Options(options_value)
- files, filtr = QFileDialog.getOpenFileNames(self,
- "QFileDialog.getOpenFileNames()", self._open_files_path,
- "All Files (*);;Text Files (*.txt)", "", options)
+ files, _ = QFileDialog.getOpenFileNames(self, "QFileDialog.getOpenFileNames()",
+ self._open_files_path,
+ "All Files (*);;Text Files (*.txt)", "", options)
if files:
self._open_files_path = files[0]
file_list = ', '.join(files)
options_value = self._file_options.value()
options = QFileDialog.Options(options_value)
- fileName, filtr = QFileDialog.getSaveFileName(self,
- "QFileDialog.getSaveFileName()",
- self._save_file_name_label.text(),
- "All Files (*);;Text Files (*.txt)", "", options)
+ fileName, _ = QFileDialog.getSaveFileName(self, "QFileDialog.getSaveFileName()",
+ self._save_file_name_label.text(),
+ "All Files (*);;Text Files (*.txt)", "", options)
if fileName:
self._save_file_name_label.setText(fileName)
page.setTitle("Introduction")
label = QLabel("This wizard will help you register your copy of "
- "Super Product Two.")
+ "Super Product Two.")
label.setWordWrap(True)
layout = QVBoxLayout(page)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x,
+ originating from PyQt"""
from PySide6.QtCore import QFile, QIODevice, QMimeData, QPoint, Qt, QTextStream
from PySide6.QtGui import QDrag, QPalette, QPixmap
from PySide6.QtWidgets import QApplication, QFrame, QLabel, QWidget
-import draggabletext_rc
+import draggabletext_rc # noqa: F401
class DragLabel(QLabel):
def setup_scene(self):
self.m_scene.setSceneRect(-300, -200, 600, 460)
- linear_grad = QLinearGradient(QPointF(-100, -100),
- QPointF(100, 100))
+ linear_grad = QLinearGradient(QPointF(-100, -100), QPointF(100, 100))
linear_grad.setColorAt(0, QColor(255, 255, 255))
linear_grad.setColorAt(1, QColor(192, 192, 255))
self.setBackgroundBrush(linear_grad)
--- /dev/null
+.. _gettext-example:
+
+GNU gettext Example
+===================
+
+This example demonstrates the use of GNU gettext for translating
+applications as described in :ref:`translations`.
f = create_item(QSizeF(30, 50), QSizeF(150, 50), max_size, "F")
g = create_item(QSizeF(30, 50), QSizeF(30, 100), max_size, "G")
- l = QGraphicsAnchorLayout()
+ l = QGraphicsAnchorLayout() # noqa: E741
l.setSpacing(0)
w = QGraphicsWidget(None, Qt.Window)
import math
import sys
-from PySide6.QtCore import (QLineF, QPointF, QRandomGenerator, QRectF, QTimer,
- Qt)
-from PySide6.QtGui import (QBrush, QColor, QPainter, QPainterPath, QPixmap,
- QPolygonF, QTransform)
-from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene,
- QGraphicsView)
+from PySide6.QtCore import (QLineF, QPointF, QRandomGenerator, QRectF, QTimer, Qt)
+from PySide6.QtGui import (QBrush, QColor, QPainter, QPainterPath, QPixmap, QPolygonF, QTransform)
+from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene, QGraphicsView)
-import mice_rc
+import mice_rc # noqa: F401
def random(boundary):
# Create the bounding rectangle once.
adjust = 0.5
BOUNDING_RECT = QRectF(-20 - adjust, -22 - adjust, 40 + adjust,
- 83 + adjust)
+ 83 + adjust)
def __init__(self):
super().__init__()
if angle_to_center < Mouse.PI and angle_to_center > Mouse.PI / 4:
# Rotate left.
self.angle += [-0.25, 0.25][self.angle < -Mouse.PI / 2]
- elif angle_to_center >= Mouse.PI and angle_to_center < (Mouse.PI + Mouse.PI / 2 + Mouse.PI / 4):
+ elif (angle_to_center >= Mouse.PI
+ and angle_to_center < (Mouse.PI + Mouse.PI / 2 + Mouse.PI / 4)):
# Rotate right.
self.angle += [-0.25, 0.25][self.angle < Mouse.PI / 2]
elif math.sin(self.angle) < 0:
QMessageBox, QSizePolicy, QToolBox, QToolButton,
QWidget)
-import diagramscene_rc
+import diagramscene_rc # noqa: F401
class Arrow(QGraphicsLineItem):
self._my_end_item = endItem
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self._my_color = Qt.black
- self.setPen(QPen(self._my_color, 2, Qt.SolidLine,
- Qt.RoundCap, Qt.RoundJoin))
+ self.setPen(QPen(self._my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
def set_color(self, color):
self._my_color = color
self._my_polygon = path.toFillPolygon()
elif self.diagram_type == self.Conditional:
self._my_polygon = QPolygonF([
- QPointF(-100, 0), QPointF(0, 100),
- QPointF(100, 0), QPointF(0, -100),
- QPointF(-100, 0)])
+ QPointF(-100, 0), QPointF(0, 100),
+ QPointF(100, 0), QPointF(0, -100),
+ QPointF(-100, 0)])
elif self.diagram_type == self.Step:
self._my_polygon = QPolygonF([
- QPointF(-100, -100), QPointF(100, -100),
- QPointF(100, 100), QPointF(-100, 100),
- QPointF(-100, -100)])
+ QPointF(-100, -100), QPointF(100, -100),
+ QPointF(100, 100), QPointF(-100, 100),
+ QPointF(-100, -100)])
else:
self._my_polygon = QPolygonF([
- QPointF(-120, -80), QPointF(-70, 80),
- QPointF(120, 80), QPointF(70, -80),
- QPointF(-120, -80)])
+ QPointF(-120, -80), QPointF(-70, 80),
+ QPointF(120, 80), QPointF(70, -80),
+ QPointF(-120, -80)])
self.setPolygon(self._my_polygon)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setPos(mouseEvent.scenePos())
self.item_inserted.emit(item)
elif self._my_mode == self.InsertLine:
- self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(),
- mouseEvent.scenePos()))
+ self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(), mouseEvent.scenePos()))
self.line.setPen(QPen(self._my_line_color, 2))
self.addItem(self.line)
elif self._my_mode == self.InsertText:
self.removeItem(self.line)
self.line = None
- if (len(start_items) and len(end_items) and
- isinstance(start_items[0], DiagramItem) and
- isinstance(end_items[0], DiagramItem) and
- start_items[0] != end_items[0]):
+ if (len(start_items) and len(end_items)
+ and isinstance(start_items[0], DiagramItem)
+ and isinstance(end_items[0], DiagramItem)
+ and start_items[0] != end_items[0]):
start_item = start_items[0]
end_item = end_items[0]
arrow = Arrow(start_item, end_item)
def text_color_changed(self):
self._text_action = self.sender()
self._font_color_tool_button.setIcon(self.create_color_tool_button_icon(
- ':/images/textpointer.png',
- QColor(self._text_action.data())))
+ ':/images/textpointer.png', QColor(self._text_action.data())))
self.text_button_triggered()
@Slot()
def item_color_changed(self):
self._fill_action = self.sender()
self._fill_color_tool_button.setIcon(self.create_color_tool_button_icon(
- ':/images/floodfill.png',
- QColor(self._fill_action.data())))
+ ':/images/floodfill.png', QColor(self._fill_action.data())))
self.fill_button_triggered()
@Slot()
def line_color_changed(self):
self._line_action = self.sender()
self._line_color_tool_button.setIcon(self.create_color_tool_button_icon(
- ':/images/linecolor.png',
- QColor(self._line_action.data())))
+ ':/images/linecolor.png', QColor(self._line_action.data())))
self.line_button_triggered()
@Slot()
@Slot()
def about(self):
QMessageBox.about(self, "About Diagram Scene",
- "The <b>Diagram Scene</b> example shows use of the graphics framework.")
+ "The <b>Diagram Scene</b> example shows use of the graphics framework.")
def create_tool_box(self):
self._button_group = QButtonGroup()
self._button_group.idClicked.connect(self.button_group_clicked)
layout = QGridLayout()
- layout.addWidget(self.create_cell_widget("Conditional", DiagramItem.Conditional),
- 0, 0)
- layout.addWidget(self.create_cell_widget("Process", DiagramItem.Step), 0,
- 1)
- layout.addWidget(self.create_cell_widget("Input/Output", DiagramItem.Io),
- 1, 0)
+ layout.addWidget(self.create_cell_widget("Conditional", DiagramItem.Conditional), 0, 0)
+ layout.addWidget(self.create_cell_widget("Process", DiagramItem.Step), 0, 1)
+ layout.addWidget(self.create_cell_widget("Input/Output", DiagramItem.Io), 1, 0)
text_button = QToolButton()
text_button.setCheckable(True)
self._background_button_group.buttonClicked.connect(self.background_button_group_clicked)
background_layout = QGridLayout()
- background_layout.addWidget(self.create_background_cell_widget("Blue Grid",
- ':/images/background1.png'), 0, 0)
- background_layout.addWidget(self.create_background_cell_widget("White Grid",
- ':/images/background2.png'), 0, 1)
- background_layout.addWidget(self.create_background_cell_widget("Gray Grid",
- ':/images/background3.png'), 1, 0)
- background_layout.addWidget(self.create_background_cell_widget("No Grid",
- ':/images/background4.png'), 1, 1)
+ background_layout.addWidget(
+ self.create_background_cell_widget("Blue Grid", ':/images/background1.png'), 0, 0)
+ background_layout.addWidget(
+ self.create_background_cell_widget("White Grid", ':/images/background2.png'), 0, 1)
+ background_layout.addWidget(
+ self.create_background_cell_widget("Gray Grid", ':/images/background3.png'), 1, 0)
+ background_layout.addWidget(
+ self.create_background_cell_widget("No Grid", ':/images/background4.png'), 1, 1)
background_layout.setRowStretch(2, 10)
background_layout.setColumnStretch(2, 10)
def create_actions(self):
self._to_front_action = QAction(
- QIcon(':/images/bringtofront.png'), "Bring to &Front",
- self, shortcut="Ctrl+F", statusTip="Bring item to front",
- triggered=self.bring_to_front)
+ QIcon(':/images/bringtofront.png'), "Bring to &Front",
+ self, shortcut="Ctrl+F", statusTip="Bring item to front",
+ triggered=self.bring_to_front)
self._send_back_action = QAction(
- QIcon(':/images/sendtoback.png'), "Send to &Back", self,
- shortcut="Ctrl+B", statusTip="Send item to back",
- triggered=self.send_to_back)
+ QIcon(':/images/sendtoback.png'), "Send to &Back", self,
+ shortcut="Ctrl+B", statusTip="Send item to back",
+ triggered=self.send_to_back)
self._delete_action = QAction(QIcon(':/images/delete.png'),
- "&Delete", self, shortcut="Delete",
- statusTip="Delete item from diagram",
- triggered=self.delete_item)
+ "&Delete", self, shortcut="Delete",
+ statusTip="Delete item from diagram",
+ triggered=self.delete_item)
self._exit_action = QAction("E&xit", self, shortcut="Ctrl+X",
- statusTip="Quit Scenediagram example", triggered=self.close)
+ statusTip="Quit Scenediagram example", triggered=self.close)
self._bold_action = QAction(QIcon(':/images/bold.png'),
- "Bold", self, checkable=True, shortcut="Ctrl+B",
- triggered=self.handle_font_change)
+ "Bold", self, checkable=True, shortcut="Ctrl+B",
+ triggered=self.handle_font_change)
self._italic_action = QAction(QIcon(':/images/italic.png'),
- "Italic", self, checkable=True, shortcut="Ctrl+I",
- triggered=self.handle_font_change)
+ "Italic", self, checkable=True, shortcut="Ctrl+I",
+ triggered=self.handle_font_change)
self._underline_action = QAction(
- QIcon(':/images/underline.png'), "Underline", self,
- checkable=True, shortcut="Ctrl+U",
- triggered=self.handle_font_change)
+ QIcon(':/images/underline.png'), "Underline", self,
+ checkable=True, shortcut="Ctrl+U",
+ triggered=self.handle_font_change)
- self._about_action = QAction("A&bout", self, shortcut="Ctrl+B",
- triggered=self.about)
+ self._about_action = QAction("A&bout", self, shortcut="Ctrl+B", triggered=self.about)
def create_menus(self):
self._file_menu = self.menuBar().addMenu("&File")
self._font_color_tool_button = QToolButton()
self._font_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
self._font_color_tool_button.setMenu(
- self.create_color_menu(self.text_color_changed, Qt.black))
+ self.create_color_menu(self.text_color_changed, Qt.black))
self._text_action = self._font_color_tool_button.menu().defaultAction()
self._font_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/textpointer.png',
- Qt.black))
+ self.create_color_tool_button_icon(':/images/textpointer.png', Qt.black))
self._font_color_tool_button.setAutoFillBackground(True)
self._font_color_tool_button.clicked.connect(self.text_button_triggered)
self._fill_color_tool_button = QToolButton()
self._fill_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
self._fill_color_tool_button.setMenu(
- self.create_color_menu(self.item_color_changed, Qt.white))
+ self.create_color_menu(self.item_color_changed, Qt.white))
self._fill_action = self._fill_color_tool_button.menu().defaultAction()
self._fill_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/floodfill.png',
- Qt.white))
+ self.create_color_tool_button_icon(':/images/floodfill.png', Qt.white))
self._fill_color_tool_button.clicked.connect(self.fill_button_triggered)
self._line_color_tool_button = QToolButton()
self._line_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
self._line_color_tool_button.setMenu(
- self.create_color_menu(self.line_color_changed, Qt.black))
+ self.create_color_menu(self.line_color_changed, Qt.black))
self._line_action = self._line_color_tool_button.menu().defaultAction()
self._line_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/linecolor.png',
- Qt.black))
+ self.create_color_tool_button_icon(':/images/linecolor.png', Qt.black))
self._line_color_tool_button.clicked.connect(self.line_button_triggered)
self._text_tool_bar = self.addToolBar("Font")
self._pointer_type_group = QButtonGroup()
self._pointer_type_group.addButton(pointer_button, DiagramScene.MoveItem)
- self._pointer_type_group.addButton(line_pointer_button,
- DiagramScene.InsertLine)
+ self._pointer_type_group.addButton(line_pointer_button, DiagramScene.InsertLine)
self._pointer_type_group.idClicked.connect(self.pointer_group_clicked)
self._scene_scale_combo = QComboBox()
color_menu = QMenu(self)
for color, name in zip(colors, names):
- action = QAction(self.create_color_icon(color), name, self,
- triggered=slot)
+ action = QAction(self.create_color_icon(color), name, self, triggered=slot)
action.setData(QColor(color))
color_menu.addAction(action)
if color == defaultColor:
QGraphicsItemAnimation, QGraphicsScene,
QGraphicsView)
-import dragdroprobot_rc
+import dragdroprobot_rc # noqa: F401
def random(boundary):
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
- if (event.mimeData().hasColor() or
- (isinstance(self, RobotHead) and event.mimeData().hasImage())):
+ if (event.mimeData().hasColor()
+ or (isinstance(self, RobotHead) and event.mimeData().hasImage())):
event.setAccepted(True)
self._drag_over = True
self.update()
def paint(self, painter, option, widget=None):
if not self.pixmap:
- painter.setBrush(self._drag_over and self.color.lighter(130)
- or self.color)
+ painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.RelativeSize)
painter.setBrush(Qt.white)
painter.drawEllipse(-7, -3 - 20, 7, 7)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130)
- or self.color)
+ or self.color)
painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.RelativeSize)
painter.drawEllipse(-25, -20, 20, 20)
painter.drawEllipse(5, -20, 20, 20)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
painter.drawRoundedRect(self.boundingRect(), 50, 50,
- Qt.RelativeSize)
+ Qt.RelativeSize)
painter.drawEllipse(-5, -5, 10, 10)
def __init__(self):
super().__init__()
- self.torsoItem = RobotTorso(self)
- self.headItem = RobotHead(self.torsoItem)
- self.upperLeftArmItem = RobotLimb(self.torsoItem)
- self.lowerLeftArmItem = RobotLimb(self.upperLeftArmItem)
+ self.torsoItem = RobotTorso(self)
+ self.headItem = RobotHead(self.torsoItem)
+ self.upperLeftArmItem = RobotLimb(self.torsoItem)
+ self.lowerLeftArmItem = RobotLimb(self.upperLeftArmItem)
self._upper_right_arm_item = RobotLimb(self.torsoItem)
self._lower_right_arm_item = RobotLimb(self._upper_right_arm_item)
self._upper_right_leg_item = RobotLimb(self.torsoItem)
self._lower_right_leg_item = RobotLimb(self._upper_right_leg_item)
- self.upperLeftLegItem = RobotLimb(self.torsoItem)
- self.lowerLeftLegItem = RobotLimb(self.upperLeftLegItem)
+ self.upperLeftLegItem = RobotLimb(self.torsoItem)
+ self.lowerLeftLegItem = RobotLimb(self.upperLeftLegItem)
self.timeline = QTimeLine()
settings = [
- # item position rotation at
- # x y time 0 / 1
- ( self.headItem, 0, -18, 20, -20 ),
- ( self.upperLeftArmItem, -15, -10, 190, 180 ),
- ( self.lowerLeftArmItem, 30, 0, 50, 10 ),
- ( self._upper_right_arm_item, 15, -10, 300, 310 ),
- ( self._lower_right_arm_item, 30, 0, 0, -70 ),
- ( self._upper_right_leg_item, 10, 32, 40, 120 ),
- ( self._lower_right_leg_item, 30, 0, 10, 50 ),
- ( self.upperLeftLegItem, -10, 32, 150, 80 ),
- ( self.lowerLeftLegItem, 30, 0, 70, 10 ),
- ( self.torsoItem, 0, 0, 5, -20 )
+ # item position rotation at
+ # x y time 0 / 1
+ (self.headItem, 0, -18, 20, -20), # noqa: E241
+ (self.upperLeftArmItem, -15, -10, 190, 180), # noqa: E241
+ (self.lowerLeftArmItem, 30, 0, 50, 10), # noqa: E241
+ (self._upper_right_arm_item, 15, -10, 300, 310), # noqa: E241
+ (self._lower_right_arm_item, 30, 0, 0, -70), # noqa: E241
+ (self._upper_right_leg_item, 10, 32, 40, 120), # noqa: E241
+ (self._lower_right_leg_item, 30, 0, 10, 50), # noqa: E241
+ (self.upperLeftLegItem, -10, 32, 150, 80), # noqa: E241
+ (self.lowerLeftLegItem, 30, 0, 70, 10), # noqa: E241
+ (self.torsoItem, 0, 0, 5, -20) # noqa: E241
]
self.animations = []
for item, pos_x, pos_y, rotation1, rotation2 in settings:
- item.setPos(pos_x,pos_y)
+ item.setPos(pos_x, pos_y)
animation = QGraphicsItemAnimation()
animation.setItem(item)
animation.setTimeLine(self.timeline)
pass
-if __name__== '__main__':
+if __name__ == '__main__':
app = QApplication(sys.argv)
scene = QGraphicsScene(-200, -200, 400, 400)
class Edge(QGraphicsItem):
- item_type = QGraphicsItem.UserType + 2
-
def __init__(self, sourceNode, destNode):
super().__init__()
self.adjust()
def item_type(self):
- return Edge.item_type
+ return QGraphicsItem.UserType + 2
def source_node(self):
return self.source()
class Node(QGraphicsItem):
- item_type = QGraphicsItem.UserType + 1
def __init__(self, graphWidget):
super().__init__()
self.setZValue(-1)
def item_type(self):
- return Node.item_type
+ return QGraphicsItem.UserType + 1
def add_edge(self, edge):
self._edge_list.append(weakref.ref(edge))
line = QLineF(self.mapFromItem(item, 0, 0), QPointF(0, 0))
dx = line.dx()
dy = line.dy()
- l = 2.0 * (dx * dx + dy * dy)
+ l = 2.0 * (dx * dx + dy * dy) # noqa: E741
if l > 0:
xvel += (dx * 150.0) / l
yvel += (dy * 150.0) / l
def boundingRect(self):
adjust = 2.0
return QRectF(-10 - adjust, -10 - adjust,
- 23 + adjust, 23 + adjust)
+ 23 + adjust, 23 + adjust)
def shape(self):
path = QPainterPath()
bottom_shadow = QRectF(scene_rect.left() + 5, scene_rect.bottom(),
scene_rect.width(), 5)
if right_shadow.intersects(rect) or right_shadow.contains(rect):
- painter.fillRect(right_shadow, Qt.darkGray)
+ painter.fillRect(right_shadow, Qt.darkGray)
if bottom_shadow.intersects(rect) or bottom_shadow.contains(rect):
- painter.fillRect(bottom_shadow, Qt.darkGray)
+ painter.fillRect(bottom_shadow, Qt.darkGray)
# Fill.
gradient = QLinearGradient(scene_rect.topLeft(), scene_rect.bottomRight())
# Text.
text_rect = QRectF(scene_rect.left() + 4, scene_rect.top() + 4,
- scene_rect.width() - 4, scene_rect.height() - 4)
+ scene_rect.width() - 4, scene_rect.height() - 4)
message = self.tr("Click and drag the nodes around, and zoom with the "
"mouse wheel or the '+' and '-' keys")
painter.drawText(text_rect, message)
def scale_view(self, scaleFactor):
- factor = self.transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width()
+ factor = self.transform().scale(scaleFactor, scaleFactor).mapRect(
+ QRectF(0, 0, 1, 1)).width()
if factor < 0.07 or factor > 100:
return
self._image_label = QLabel()
self._image_label.setBackgroundRole(QPalette.Base)
self._image_label.setSizePolicy(QSizePolicy.Ignored,
- QSizePolicy.Ignored)
+ QSizePolicy.Ignored)
self._image_label.setScaledContents(True)
self._scroll_area = QScrollArea()
name_label = QLabel("Name")
address_label = QLabel("Address")
- button_box = QDialogButtonBox(QDialogButtonBox.Ok |
- QDialogButtonBox.Cancel)
+ button_box = QDialogButtonBox(QDialogButtonBox.Ok
+ | QDialogButtonBox.Cancel)
self._name_text = QLineEdit()
self._address_text = QTextEdit()
tool_menu = self.menuBar().addMenu("&Tools")
# Populate the File menu
- open_action = self.create_action("&Open...", file_menu, self.open_file)
- save_action = self.create_action("&Save As...", file_menu, self.save_file)
+ self.open_action = self.create_action("&Open...", file_menu, self.open_file)
+ self.save_action = self.create_action("&Save As...", file_menu, self.save_file)
file_menu.addSeparator()
- exit_action = self.create_action("E&xit", file_menu, self.close)
+ self.exit_action = self.create_action("E&xit", file_menu, self.close)
# Populate the Tools menu
- add_action = self.create_action("&Add Entry...", tool_menu, self._address_widget.add_entry)
- self._edit_action = self.create_action("&Edit Entry...", tool_menu, self._address_widget.edit_entry)
+ self.add_action = self.create_action(
+ "&Add Entry...", tool_menu, self._address_widget.add_entry)
+ self._edit_action = self.create_action(
+ "&Edit Entry...", tool_menu, self._address_widget.edit_entry)
tool_menu.addSeparator()
- self._remove_action = self.create_action("&Remove Entry", tool_menu, self._address_widget.remove_entry)
+ self._remove_action = self.create_action(
+ "&Remove Entry", tool_menu, self._address_widget.remove_entry)
# Disable the edit and remove menu items initially, as there are
# no items yet.
proxy_model.setFilterKeyColumn(0) # Filter on the "name" column
proxy_model.sort(0, Qt.AscendingOrder)
- # This prevents an application crash (see: https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash)
- viewselectionmodel = table_view.selectionModel()
+ # This prevents an application crash (see:
+ # https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) # noqa: E501
+ self.viewselectionmodel = table_view.selectionModel()
table_view.selectionModel().selectionChanged.connect(self.selection_changed)
self.addTab(table_view, group)
"""
if not index.isValid():
return Qt.ItemIsEnabled
- return Qt.ItemFlags(QAbstractTableModel.flags(self, index) |
- Qt.ItemIsEditable)
+ return Qt.ItemFlags(QAbstractTableModel.flags(self, index)
+ | Qt.ItemIsEditable)
self._filter_syntax_combo_box = QComboBox()
self._filter_syntax_combo_box.addItem("Regular expression",
- REGULAR_EXPRESSION)
+ REGULAR_EXPRESSION)
self._filter_syntax_combo_box.addItem("Wildcard",
- WILDCARD)
+ WILDCARD)
self._filter_syntax_combo_box.addItem("Fixed string",
- FIXED_STRING)
+ FIXED_STRING)
self._filter_syntax_label = QLabel("Filter &syntax:")
self._filter_syntax_label.setBuddy(self._filter_syntax_combo_box)
model.setHeaderData(2, Qt.Horizontal, "Date")
add_mail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
- QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
+ QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
add_mail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
- QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
+ QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
add_mail(model, "Accounts", "pascale@nospam.com",
- QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
+ QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
add_mail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
- QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
+ QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
add_mail(model, "Re: Expenses", "Andy <andy@nospam.com>",
- QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
+ QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
add_mail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
- QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
+ QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
add_mail(model, "Re: Accounts", "Andy <andy@nospam.com>",
- QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
+ QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
add_mail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
- QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
+ QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
add_mail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
- QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
+ QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
add_mail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
- QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
+ QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
return model
tree.show()
sys.exit(app.exec())
-
self.insert_child_action.setShortcut("Ctrl+N")
self.insert_child_action.triggered.connect(self.insert_child)
help_menu = menubar.addMenu("&Help")
- about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt)
+ about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt) # noqa: F821
about_qt_action.setShortcut("F1")
self.setWindowTitle("Editable Tree Model")
if role == Qt.BackgroundRole:
batch = row // BATCH_SIZE
- palette = qApp.palette()
+ palette = qApp.palette() # noqa: F821
return palette.base() if batch % 2 == 0 else palette.alternateBase()
if role == Qt.DecorationRole:
self.setWindowTitle("Fetch More Example")
- @Slot(str,int,int,int)
+ @Slot(str, int, int, int)
def update_log(self, path, start, number, total):
native_path = QDir.toNativeSeparators(path)
last = start + number - 1
"""PySide6 port of the widgets/itemviews/spinboxdelegate from Qt v6.x"""
+
#! [0]
class SpinBoxDelegate(QStyledItemDelegate):
"""A delegate that allows the user to change integer values from the model
if __name__ == '__main__':
app = QApplication(sys.argv)
- model= QStandardItemModel(4, 2)
+ model = QStandardItemModel(4, 2)
tableView = QTableView()
tableView.setModel(model)
# Create and populate the tableWidget
table_widget = QTableWidget(4, 4)
table_widget.setItemDelegate(StarDelegate())
- table_widget.setEditTriggers(QAbstractItemView.DoubleClicked |
- QAbstractItemView.SelectedClicked)
+ table_widget.setEditTriggers(QAbstractItemView.DoubleClicked
+ | QAbstractItemView.SelectedClicked)
table_widget.setSelectionBehavior(QAbstractItemView.SelectRows)
table_widget.setHorizontalHeaderLabels(["Title", "Genre", "Artist", "Rating"])
- data = [ ["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
- ["Three More Foxes", "Jazz", "Maynard Ferguson", 4],
- ["Sex Bomb", "Pop", "Tom Jones", 3],
- ["Barbie Girl", "Pop", "Aqua", 5] ]
+ data = [["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
+ ["Three More Foxes", "Jazz", "Maynard Ferguson", 4],
+ ["Sex Bomb", "Pop", "Tom Jones", 3],
+ ["Barbie Girl", "Pop", "Aqua", 5]]
for r in range(len(data)):
table_widget.setItem(r, 0, QTableWidgetItem(data[r][0]))
""" Calculate which star the user's mouse cursor is currently
hovering over.
"""
- star = (x / (self.star_rating.sizeHint().width() /
- self.star_rating.MAX_STAR_COUNT)) + 1
+ star = (x / (self.star_rating.sizeHint().width() / self.star_rating.MAX_STAR_COUNT)) + 1
if (star <= 0) or (star > self.star_rating.MAX_STAR_COUNT):
return -1
self._star_polygon.append(QPointF(1.0, 0.5))
for i in range(1, 5):
self._star_polygon.append(QPointF(0.5 + 0.5 * cos(0.8 * i * pi),
- 0.5 + 0.5 * sin(0.8 * i * pi)))
+ 0.5 + 0.5 * sin(0.8 * i * pi)))
# Create the diamond shape we'll show in the editor
self._diamond_polygon = QPolygonF()
diamond_points = [QPointF(0.4, 0.5), QPointF(0.5, 0.4),
- QPointF(0.6, 0.5), QPointF(0.5, 0.6),
- QPointF(0.4, 0.5)]
+ QPointF(0.6, 0.5), QPointF(0.5, 0.6),
+ QPointF(0.4, 0.5)]
self._diamond_polygon.append(diamond_points)
def sizeHint(self):
big_editor = QTextEdit()
big_editor.setPlainText("This widget takes up all the remaining space "
- "in the top-level layout.")
+ "in the top-level layout.")
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
layout.addWidget(line_edit, i + 1, 1)
self._small_editor = QTextEdit()
- self._small_editor.setPlainText("This widget takes up about two thirds "
- "of the grid layout.")
+ self._small_editor.setPlainText("This widget takes up about two thirds of the grid layout.")
layout.addWidget(self._small_editor, 0, 2, 4, 1)
def show_help(self):
QMessageBox.information(self, "Dynamic Layouts Help",
- "This example shows how to change layouts "
- "dynamically.")
+ "This example shows how to change layouts "
+ "dynamically.")
def create_rotable_group_box(self):
self._rotable_group_box = QGroupBox("Rotable Widgets")
buttons_orientation_combo_box = QComboBox()
buttons_orientation_combo_box.addItem("Horizontal", Qt.Horizontal)
buttons_orientation_combo_box.addItem("Vertical", Qt.Vertical)
- buttons_orientation_combo_box.currentIndexChanged[int].connect(self.buttons_orientation_changed)
+ buttons_orientation_combo_box.currentIndexChanged[int].connect(
+ self.buttons_orientation_changed)
self._buttons_orientation_combo_box = buttons_orientation_combo_box
close_button = self._button_box.addButton(QDialogButtonBox.Close)
help_button = self._button_box.addButton(QDialogButtonBox.Help)
- rotate_widgets_button = self._button_box.addButton("Rotate &Widgets", QDialogButtonBox.ActionRole)
+ rotate_widgets_button = self._button_box.addButton(
+ "Rotate &Widgets", QDialogButtonBox.ActionRole)
rotate_widgets_button.clicked.connect(self.rotate_widgets)
close_button.clicked.connect(self.close)
--- /dev/null
+.. _qt-linguist-example:
+
+Qt Linguist Example
+===================
+
+This example demonstrates the use of Qt Linguist and related tools for translating
+applications as described in :ref:`translations`.
QMainWindow)
-import linguist_rc
+import linguist_rc # noqa: F401
class Window(QMainWindow):
quit_action.triggered.connect(self.close)
help_menu = self.menuBar().addMenu(self.tr("&Help"))
about_qt_action = help_menu.addAction(self.tr("About Qt"))
- about_qt_action.triggered.connect(qApp.aboutQt)
+ about_qt_action.triggered.connect(qApp.aboutQt) # noqa: F821
self._list_widget = QListWidget()
self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
QMessageBox, QTextEdit)
-import application_rc
+import application_rc # noqa: F401
class MainWindow(QMainWindow):
@Slot()
def about(self):
QMessageBox.about(self, "About Application",
- "The <b>Application</b> example demonstrates how to write "
- "modern GUI applications using Qt, with a menu bar, "
- "toolbars, and a status bar.")
+ "The <b>Application</b> example demonstrates how to write "
+ "modern GUI applications using Qt, with a menu bar, "
+ "toolbars, and a status bar.")
@Slot()
def document_was_modified(self):
def create_actions(self):
icon = QIcon.fromTheme("document-new", QIcon(':/images/new.png'))
self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.New,
- statusTip="Create a new file", triggered=self.new_file)
+ statusTip="Create a new file", triggered=self.new_file)
icon = QIcon.fromTheme("document-open", QIcon(':/images/open.png'))
self._open_act = QAction(icon, "&Open...", self,
- shortcut=QKeySequence.Open, statusTip="Open an existing file",
- triggered=self.open)
+ shortcut=QKeySequence.Open, statusTip="Open an existing file",
+ triggered=self.open)
icon = QIcon.fromTheme("document-save", QIcon(':/images/save.png'))
self._save_act = QAction(icon, "&Save", self,
- shortcut=QKeySequence.Save,
- statusTip="Save the document to disk", triggered=self.save)
+ shortcut=QKeySequence.Save,
+ statusTip="Save the document to disk", triggered=self.save)
self._save_as_act = QAction("Save &As...", self,
- shortcut=QKeySequence.SaveAs,
- statusTip="Save the document under a new name",
- triggered=self.save_as)
+ shortcut=QKeySequence.SaveAs,
+ statusTip="Save the document under a new name",
+ triggered=self.save_as)
self._exit_act = QAction("E&xit", self, shortcut="Ctrl+Q",
- statusTip="Exit the application", triggered=self.close)
+ statusTip="Exit the application", triggered=self.close)
icon = QIcon.fromTheme("edit-cut", QIcon(':/images/cut.png'))
self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.Cut,
- statusTip="Cut the current selection's contents to the clipboard",
- triggered=self._text_edit.cut)
+ statusTip="Cut the current selection's contents to the clipboard",
+ triggered=self._text_edit.cut)
icon = QIcon.fromTheme("edit-copy", QIcon(':/images/copy.png'))
self._copy_act = QAction(icon, "&Copy",
- self, shortcut=QKeySequence.Copy,
- statusTip="Copy the current selection's contents to the clipboard",
- triggered=self._text_edit.copy)
+ self, shortcut=QKeySequence.Copy,
+ statusTip="Copy the current selection's contents to the clipboard",
+ triggered=self._text_edit.copy)
icon = QIcon.fromTheme("edit-paste", QIcon(':/images/paste.png'))
self._paste_act = QAction(icon, "&Paste",
- self, shortcut=QKeySequence.Paste,
- statusTip="Paste the clipboard's contents into the current selection",
- triggered=self._text_edit.paste)
+ self, shortcut=QKeySequence.Paste,
+ statusTip="Paste the clipboard's contents into the current "
+ "selection",
+ triggered=self._text_edit.paste)
self._about_act = QAction("&About", self,
- statusTip="Show the application's About box",
- triggered=self.about)
+ statusTip="Show the application's About box",
+ triggered=self.about)
self._about_qt_act = QAction("About &Qt", self,
- statusTip="Show the Qt library's About box",
- triggered=qApp.aboutQt)
+ statusTip="Show the Qt library's About box",
+ triggered=qApp.aboutQt) # noqa: F821
self._cut_act.setEnabled(False)
self._copy_act.setEnabled(False)
def maybe_save(self):
if self._text_edit.document().isModified():
ret = QMessageBox.warning(self, "Application",
- "The document has been modified.\nDo you want to save "
- "your changes?",
- QMessageBox.Save | QMessageBox.Discard |
- QMessageBox.Cancel)
+ "The document has been modified.\nDo you want to save "
+ "your changes?",
+ QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
if ret == QMessageBox.Save:
return self.save()
elif ret == QMessageBox.Cancel:
file = QFile(fileName)
if not file.open(QFile.ReadOnly | QFile.Text):
reason = file.errorString()
- QMessageBox.warning(self, "Application",
- f"Cannot read file {fileName}:\n{reason}.")
+ QMessageBox.warning(self, "Application", f"Cannot read file {fileName}:\n{reason}.")
return
inf = QTextStream(file)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x,
+ originating from PyQt"""
import sys
from PySide6.QtCore import QDate, QFile, Qt, QTextStream
from PySide6.QtGui import (QAction, QFont, QIcon, QKeySequence,
- QTextCharFormat, QTextCursor, QTextTableFormat)
+ QTextCharFormat, QTextCursor, QTextTableFormat)
from PySide6.QtPrintSupport import QPrintDialog, QPrinter
from PySide6.QtWidgets import (QApplication, QDialog, QDockWidget,
- QFileDialog, QListWidget, QMainWindow, QMessageBox, QTextEdit)
+ QFileDialog, QListWidget, QMainWindow,
+ QMessageBox, QTextEdit)
-import dockwidgets_rc
+import dockwidgets_rc # noqa: F401
class MainWindow(QMainWindow):
cursor.insertBlock()
cursor.insertText("Some Country")
cursor.setPosition(top_frame.lastPosition())
- cursor.insertText(QDate.currentDate().toString("d MMMM yyyy"),
- text_format)
+ cursor.insertText(QDate.currentDate().toString("d MMMM yyyy"), text_format)
cursor.insertBlock()
cursor.insertBlock()
cursor.insertText("Dear ", text_format)
if not file.open(QFile.WriteOnly | QFile.Text):
reason = file.errorString()
QMessageBox.warning(self, "Dock Widgets",
- f"Cannot write file {filename}:\n{reason}.")
+ f"Cannot write file {filename}:\n{reason}.")
return
out = QTextStream(file)
if cursor.isNull():
return
cursor.beginEditBlock()
- cursor.movePosition(QTextCursor.PreviousBlock, QTextCursor.MoveAnchor,
- 2)
+ cursor.movePosition(QTextCursor.PreviousBlock,
+ QTextCursor.MoveAnchor, 2)
cursor.insertBlock()
cursor.insertText(paragraph)
cursor.insertBlock()
def about(self):
QMessageBox.about(self, "About Dock Widgets",
- "The <b>Dock Widgets</b> example demonstrates how to use "
- "Qt's dock widgets. You can enter your own text, click a "
- "customer to add a customer name and address, and click "
- "standard paragraphs to add them.")
+ "The <b>Dock Widgets</b> example demonstrates how to use "
+ "Qt's dock widgets. You can enter your own text, click a "
+ "customer to add a customer name and address, and click "
+ "standard paragraphs to add them.")
def create_actions(self):
icon = QIcon.fromTheme('document-new', QIcon(':/images/new.png'))
self._new_letter_act = QAction(icon, "&New Letter",
- self, shortcut=QKeySequence.New,
- statusTip="Create a new form letter", triggered=self.new_letter)
+ self, shortcut=QKeySequence.New,
+ statusTip="Create a new form letter",
+ triggered=self.new_letter)
icon = QIcon.fromTheme('document-save', QIcon(':/images/save.png'))
self._save_act = QAction(icon, "&Save...", self,
- shortcut=QKeySequence.Save,
- statusTip="Save the current form letter", triggered=self.save)
+ shortcut=QKeySequence.Save,
+ statusTip="Save the current form letter", triggered=self.save)
icon = QIcon.fromTheme('document-print', QIcon(':/images/print.png'))
self._print_act = QAction(icon, "&Print...", self,
- shortcut=QKeySequence.Print,
- statusTip="Print the current form letter",
- triggered=self.print_)
+ shortcut=QKeySequence.Print,
+ statusTip="Print the current form letter",
+ triggered=self.print_)
icon = QIcon.fromTheme('edit-undo', QIcon(':/images/undo.png'))
self._undo_act = QAction(icon, "&Undo", self,
- shortcut=QKeySequence.Undo,
- statusTip="Undo the last editing action", triggered=self.undo)
+ shortcut=QKeySequence.Undo,
+ statusTip="Undo the last editing action", triggered=self.undo)
self._quit_act = QAction("&Quit", self, shortcut="Ctrl+Q",
- statusTip="Quit the application", triggered=self.close)
+ statusTip="Quit the application", triggered=self.close)
self._about_act = QAction("&About", self,
- statusTip="Show the application's About box",
- triggered=self.about)
+ statusTip="Show the application's About box",
+ triggered=self.about)
self._about_qt_act = QAction("About &Qt", self,
- statusTip="Show the Qt library's About box",
- triggered=QApplication.instance().aboutQt)
+ statusTip="Show the Qt library's About box",
+ triggered=QApplication.instance().aboutQt)
def create_menus(self):
self._file_menu = self.menuBar().addMenu("&File")
self._paragraphs_list.addItems((
"Thank you for your payment which we have received today.",
"Your order has been dispatched and should be with you within "
- "28 days.",
+ "28 days.",
"We have dispatched those items that were in stock. The rest of "
- "your order will be dispatched once all the remaining items "
- "have arrived at our warehouse. No additional shipping "
- "charges will be made.",
+ "your order will be dispatched once all the remaining items "
+ "have arrived at our warehouse. No additional shipping "
+ "charges will be made.",
"You made a small overpayment (less than $5) which we will keep "
- "on account for you, or return at your request.",
+ "on account for you, or return at your request.",
"You made a small underpayment (less than $1), but we have sent "
- "your order anyway. We'll add this underpayment to your next "
- "bill.",
+ "your order anyway. We'll add this underpayment to your next "
+ "bill.",
"Unfortunately you did not send enough money. Please remit an "
- "additional $. Your order will be dispatched as soon as the "
- "complete amount has been received.",
+ "additional $. Your order will be dispatched as soon as the "
+ "complete amount has been received.",
"You made an overpayment (more than $5). Do you wish to buy more "
- "items, or should we return the excess to you?"))
+ "items, or should we return the excess to you?"))
dock.setWidget(self._paragraphs_list)
self.addDockWidget(Qt.RightDockWidgetArea, dock)
self._view_menu.addAction(dock.toggleViewAction())
import sys
from PySide6.QtCore import (QByteArray, QFile, QFileInfo, QSettings,
- QSaveFile, QTextStream, Qt, Slot)
+ QSaveFile, QTextStream, Qt, Slot)
from PySide6.QtGui import QAction, QIcon, QKeySequence
from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
- QMdiArea, QMessageBox, QTextEdit)
+ QMdiArea, QMessageBox, QTextEdit)
-import PySide6.QtExampleIcons
+import PySide6.QtExampleIcons # noqa: F401
class MdiChild(QTextEdit):
f = self.user_friendly_current_file()
message = f"'{f}' has been modified.\nDo you want to save your changes?"
ret = QMessageBox.warning(self, "MDI", message,
- QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
+ QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
if ret == QMessageBox.Save:
return self.save()
@Slot()
def about(self):
QMessageBox.about(self, "About MDI",
- "The <b>MDI</b> example demonstrates how to write multiple "
- "document interface applications using Qt.")
+ "The <b>MDI</b> example demonstrates how to write multiple "
+ "document interface applications using Qt.")
@Slot()
def update_menus(self):
self._previous_act.setEnabled(has_mdi_child)
self._separator_act.setVisible(has_mdi_child)
- has_selection = (self.active_mdi_child() is not None and
- self.active_mdi_child().textCursor().hasSelection())
+ has_selection = (self.active_mdi_child() is not None
+ and self.active_mdi_child().textCursor().hasSelection())
self._cut_act.setEnabled(has_selection)
self._copy_act.setEnabled(has_selection)
icon = QIcon.fromTheme("document-new")
self._new_act = QAction(icon, "&New", self,
- shortcut=QKeySequence.New, statusTip="Create a new file",
- triggered=self.new_file)
+ shortcut=QKeySequence.New, statusTip="Create a new file",
+ triggered=self.new_file)
icon = QIcon.fromTheme("document-open")
self._open_act = QAction(icon, "&Open...", self,
- shortcut=QKeySequence.Open, statusTip="Open an existing file",
- triggered=self.open)
+ shortcut=QKeySequence.Open, statusTip="Open an existing file",
+ triggered=self.open)
icon = QIcon.fromTheme("document-save")
self._save_act = QAction(icon, "&Save", self,
- shortcut=QKeySequence.Save,
- statusTip="Save the document to disk", triggered=self.save)
+ shortcut=QKeySequence.Save,
+ statusTip="Save the document to disk", triggered=self.save)
self._save_as_act = QAction("Save &As...", self,
- shortcut=QKeySequence.SaveAs,
- statusTip="Save the document under a new name",
- triggered=self.save_as)
+ shortcut=QKeySequence.SaveAs,
+ statusTip="Save the document under a new name",
+ triggered=self.save_as)
self._exit_act = QAction("E&xit", self, shortcut=QKeySequence.Quit,
- statusTip="Exit the application",
- triggered=QApplication.instance().closeAllWindows)
+ statusTip="Exit the application",
+ triggered=QApplication.instance().closeAllWindows)
icon = QIcon.fromTheme("edit-cut")
self._cut_act = QAction(icon, "Cu&t", self,
- shortcut=QKeySequence.Cut,
- statusTip="Cut the current selection's contents to the clipboard",
- triggered=self.cut)
+ shortcut=QKeySequence.Cut,
+ statusTip="Cut the current selection's contents to the clipboard",
+ triggered=self.cut)
icon = QIcon.fromTheme("edit-copy")
self._copy_act = QAction(icon, "&Copy", self,
- shortcut=QKeySequence.Copy,
- statusTip="Copy the current selection's contents to the clipboard",
- triggered=self.copy)
+ shortcut=QKeySequence.Copy,
+ statusTip="Copy the current selection's contents to the clipboard",
+ triggered=self.copy)
icon = QIcon.fromTheme("edit-paste")
self._paste_act = QAction(icon, "&Paste", self,
- shortcut=QKeySequence.Paste,
- statusTip="Paste the clipboard's contents into the current selection",
- triggered=self.paste)
+ shortcut=QKeySequence.Paste,
+ statusTip="Paste the clipboard's contents into the current "
+ "selection",
+ triggered=self.paste)
self._close_act = QAction("Cl&ose", self,
- statusTip="Close the active window",
- triggered=self._mdi_area.closeActiveSubWindow)
+ statusTip="Close the active window",
+ triggered=self._mdi_area.closeActiveSubWindow)
self._close_all_act = QAction("Close &All", self,
- statusTip="Close all the windows",
- triggered=self._mdi_area.closeAllSubWindows)
+ statusTip="Close all the windows",
+ triggered=self._mdi_area.closeAllSubWindows)
self._tile_act = QAction("&Tile", self, statusTip="Tile the windows",
- triggered=self._mdi_area.tileSubWindows)
+ triggered=self._mdi_area.tileSubWindows)
self._cascade_act = QAction("&Cascade", self,
- statusTip="Cascade the windows",
- triggered=self._mdi_area.cascadeSubWindows)
+ statusTip="Cascade the windows",
+ triggered=self._mdi_area.cascadeSubWindows)
self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
- statusTip="Move the focus to the next window",
- triggered=self._mdi_area.activateNextSubWindow)
+ statusTip="Move the focus to the next window",
+ triggered=self._mdi_area.activateNextSubWindow)
self._previous_act = QAction("Pre&vious", self,
- shortcut=QKeySequence.PreviousChild,
- statusTip="Move the focus to the previous window",
- triggered=self._mdi_area.activatePreviousSubWindow)
+ shortcut=QKeySequence.PreviousChild,
+ statusTip="Move the focus to the previous window",
+ triggered=self._mdi_area.activatePreviousSubWindow)
self._separator_act = QAction(self)
self._separator_act.setSeparator(True)
self._about_act = QAction("&About", self,
- statusTip="Show the application's About box",
- triggered=self.about)
+ statusTip="Show the application's About box",
+ triggered=self.about)
self._about_qt_act = QAction("About &Qt", self,
- statusTip="Show the Qt library's About box",
- triggered=QApplication.instance().aboutQt)
+ statusTip="Show the Qt library's About box",
+ triggered=QApplication.instance().aboutQt)
def create_menus(self):
self._file_menu = self.menuBar().addMenu("&File")
icon_paths = QIcon.themeSearchPaths()
QIcon.setThemeSearchPaths(icon_paths + [":/qt-project.org/icons"])
- QIcon.setFallbackThemeName("example_icons");
+ QIcon.setFallbackThemeName("example_icons")
main_win = MainWindow()
for f in options.files:
from PySide6.QtCore import QPoint, QRect, QSize, Qt, qVersion
from PySide6.QtGui import (QBrush, QConicalGradient, QLinearGradient, QPainter,
- QPainterPath, QPalette, QPen, QPixmap, QPolygon, QRadialGradient)
+ QPainterPath, QPalette, QPen, QPixmap, QPolygon,
+ QRadialGradient)
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout,
- QLabel, QSpinBox, QWidget)
+ QLabel, QSpinBox, QWidget)
-import basicdrawing_rc
+import basicdrawing_rc # noqa: F401
class RenderArea(QWidget):
pen_join_label.setBuddy(self._pen_join_combo_box)
self._brush_style_combo_box = QComboBox()
- self._brush_style_combo_box.addItem("Linear Gradient",
- Qt.LinearGradientPattern)
- self._brush_style_combo_box.addItem("Radial Gradient",
- Qt.RadialGradientPattern)
- self._brush_style_combo_box.addItem("Conical Gradient",
- Qt.ConicalGradientPattern)
+ self._brush_style_combo_box.addItem("Linear Gradient", Qt.LinearGradientPattern)
+ self._brush_style_combo_box.addItem("Radial Gradient", Qt.RadialGradientPattern)
+ self._brush_style_combo_box.addItem("Conical Gradient", Qt.ConicalGradientPattern)
self._brush_style_combo_box.addItem("Texture", Qt.TexturePattern)
self._brush_style_combo_box.addItem("Solid", Qt.SolidPattern)
self._brush_style_combo_box.addItem("Horizontal", Qt.HorPattern)
self.setWindowTitle("Basic Drawing")
def shape_changed(self):
- shape = self._shape_combo_box.itemData(self._shape_combo_box.currentIndex(),
- id_role)
+ shape = self._shape_combo_box.itemData(self._shape_combo_box.currentIndex(), id_role)
self._render_area.set_shape(shape)
def pen_changed(self):
width = self._pen_width_spin_box.value()
style = Qt.PenStyle(self._pen_style_combo_box.itemData(
- self._pen_style_combo_box.currentIndex(), id_role))
+ self._pen_style_combo_box.currentIndex(), id_role))
cap = Qt.PenCapStyle(self._pen_cap_combo_box.itemData(
- self._pen_cap_combo_box.currentIndex(), id_role))
+ self._pen_cap_combo_box.currentIndex(), id_role))
join = Qt.PenJoinStyle(self._pen_join_combo_box.itemData(
- self._pen_join_combo_box.currentIndex(), id_role))
+ self._pen_join_combo_box.currentIndex(), id_role))
self._render_area.set_pen(QPen(Qt.blue, width, style, cap, join))
def brush_changed(self):
style = Qt.BrushStyle(self._brush_style_combo_box.itemData(
- self._brush_style_combo_box.currentIndex(), id_role))
+ self._brush_style_combo_box.currentIndex(), id_role))
if style == Qt.LinearGradientPattern:
linear_gradient = QLinearGradient(0, 0, 100, 100)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating
+ from PyQt"""
from PySide6.QtCore import QRect, QRectF, QSize, Qt, QTimer
from PySide6.QtGui import QColor, QPainter, QPalette, QPen
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel,
- QSizePolicy, QWidget)
+ QSizePolicy, QWidget)
class CircleWidget(QWidget):
if self._float_based:
painter.drawEllipse(QRectF(-diameter / 2.0,
- -diameter / 2.0, diameter, diameter))
+ -diameter / 2.0, diameter, diameter))
else:
painter.drawEllipse(QRect(-diameter / 2,
- -diameter / 2, diameter, diameter))
+ -diameter / 2, diameter, diameter))
class Window(QWidget):
self.bar = self.addToolBar("Menu")
self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
self._save_action = self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogSaveButton), "Save", self.on_save
+ qApp.style().standardIcon(QStyle.SP_DialogSaveButton), # noqa: F821
+ "Save", self.on_save
)
self._save_action.setShortcut(QKeySequence.Save)
self._open_action = self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogOpenButton), "Open", self.on_open
+ qApp.style().standardIcon(QStyle.SP_DialogOpenButton), # noqa: F821
+ "Open", self.on_open
)
self._open_action.setShortcut(QKeySequence.Open)
self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogResetButton),
+ qApp.style().standardIcon(QStyle.SP_DialogResetButton), # noqa: F821
"Clear",
self.painter_widget.clear,
)
body_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 100))
cursor.insertFrame(body_frame_format)
- cursor.insertText("I would like to place an order for the following "
- "items:", text_format)
+ cursor.insertText("I would like to place an order for the following items:", text_format)
cursor.insertBlock()
cursor.insertBlock()
cursor.insertBlock()
cursor.insertText("Please update my records to take account of the "
- "following privacy information:")
+ "following privacy information:")
cursor.insertBlock()
offers_table = cursor.insertTable(2, 2)
cursor = offers_table.cellAt(0, 1).firstCursorPosition()
cursor.insertText("I want to receive more information about your "
- "company's products and special offers.", text_format)
+ "company's products and special offers.", text_format)
cursor = offers_table.cellAt(1, 1).firstCursorPosition()
cursor.insertText("I do not want to receive any promotional "
- "information from your company.", text_format)
+ "information from your company.", text_format)
if sendOffers:
cursor = offers_table.cellAt(0, 0).firstCursorPosition()
def create_sample(self):
dialog = DetailsDialog('Dialog with default values', self)
self.create_letter('Mr Smith',
- '12 High Street\nSmall Town\nThis country',
- dialog.order_items(), True)
+ '12 High Street\nSmall Town\nThis country',
+ dialog.order_items(), True)
@Slot()
def open_dialog(self):
if dialog.exec() == QDialog.Accepted:
self.create_letter(dialog.sender_name(), dialog.sender_address(),
- dialog.order_items(), dialog.send_offers())
+ dialog.order_items(), dialog.send_offers())
@Slot()
def print_file(self):
self._name_edit = QLineEdit()
self._address_edit = QTextEdit()
- self._offers_check_box = QCheckBox("Send information about "
- "products and special offers:")
+ self._offers_check_box = QCheckBox("Send information about products and special offers:")
self.setup_items_table()
return
answer = QMessageBox.warning(self, "Incomplete Form",
- "The form does not contain all the necessary information.\n"
- "Do you want to discard it?",
- QMessageBox.Yes, QMessageBox.No)
+ "The form does not contain all the necessary information.\n"
+ "Do you want to discard it?",
+ QMessageBox.Yes, QMessageBox.No)
if answer == QMessageBox.Yes:
self.reject()
from PySide6.QtGui import (QColor, QFont, QFontDatabase, QKeySequence,
QSyntaxHighlighter, QTextCharFormat)
from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
- QPlainTextEdit)
+ QPlainTextEdit)
class MainWindow(QMainWindow):
quit_act.triggered.connect(self.close)
help_menu = self.menuBar().addMenu("&Help")
- help_menu.addAction("About &Qt", qApp.aboutQt)
+ help_menu.addAction("About &Qt", qApp.aboutQt) # noqa: F821
class Highlighter(QSyntaxHighlighter):
from textedit import TextEdit
-import textedit_rc
+import textedit_rc # noqa: F401
if __name__ == '__main__':
help_menu = self.menuBar().addMenu("Help")
help_menu.addAction("About", self.about)
- help_menu.addAction("About &Qt", qApp.aboutQt)
+ help_menu.addAction("About &Qt", qApp.aboutQt) # noqa: F821
text_font = QFont("Helvetica")
text_font.setStyleHint(QFont.SansSerif)
above = QTextCursor(cursor)
above.movePosition(QTextCursor.Up)
if (above.currentList()
- and list_fmt.indent() + amount == above.currentList().format().indent()):
+ and list_fmt.indent() + amount == above.currentList().format().indent()):
above.currentList().add(cursor.block())
else:
list_fmt.setIndent(list_fmt.indent() + amount)
import logging
from PySide6.QtCore import (QMargins, QRegularExpression, QRegularExpressionMatch,
- QRegularExpressionMatchIterator, Qt, Slot,)
+ QRegularExpressionMatchIterator, Qt, Slot,)
from PySide6.QtGui import (QAction, QColor, QContextMenuEvent, QFontDatabase,
- QGuiApplication, QIcon, QPalette,)
+ QGuiApplication, QIcon, QPalette,)
from PySide6.QtWidgets import (QCheckBox, QComboBox, QDialog, QFormLayout,
- QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QPlainTextEdit,
- QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget,)
+ QFrame, QGridLayout, QHBoxLayout, QLabel,
+ QLineEdit, QPlainTextEdit, QSpinBox,
+ QTreeWidget, QTreeWidgetItem, QVBoxLayout,
+ QWidget)
def rawStringLiteral(pattern: str) -> str:
if code_characters[index] == '\\':
del code_characters[index]
code_characters_size -= 1
- index +=1
+ index += 1
code = "".join(code_characters)
if code.startswith('"') and code.endswith('"'):
t = (
t[: selection_start]
+ escapedSelection
- + t[selection_start + len(selection) :]
+ + t[selection_start + len(selection):]
)
self.setText(t)
self.patternOptionsCheckBoxLayout = QGridLayout()
gridRow = 0
- self.patternOptionsCheckBoxLayout.addWidget(self.caseInsensitiveOptionCheckBox, gridRow, \
- 1)
- self.patternOptionsCheckBoxLayout.addWidget(self.dotMatchesEverythingOptionCheckBox, gridRow\
- ,2)
+ self.patternOptionsCheckBoxLayout.addWidget(self.caseInsensitiveOptionCheckBox, gridRow, 1)
+ self.patternOptionsCheckBoxLayout.addWidget(
+ self.dotMatchesEverythingOptionCheckBox, gridRow, 2)
gridRow = gridRow + 1
- self.patternOptionsCheckBoxLayout.addWidget(self.multilineOptionCheckBox, gridRow, \
- 1)
- self.patternOptionsCheckBoxLayout.addWidget(self.extendedPatternSyntaxOptionCheckBox, gridRow \
- , 2)
+ self.patternOptionsCheckBoxLayout.addWidget(self.multilineOptionCheckBox, gridRow, 1)
+ self.patternOptionsCheckBoxLayout.addWidget(
+ self.extendedPatternSyntaxOptionCheckBox, gridRow, 2)
gridRow = gridRow + 1
- self.patternOptionsCheckBoxLayout.addWidget(self.invertedGreedinessOptionCheckBox, gridRow,\
- 1)
- self.patternOptionsCheckBoxLayout.addWidget(self.dontCaptureOptionCheckBox, gridRow,\
- 2)
+ self.patternOptionsCheckBoxLayout.addWidget(
+ self.invertedGreedinessOptionCheckBox, gridRow, 1)
+ self.patternOptionsCheckBoxLayout.addWidget(self.dontCaptureOptionCheckBox, gridRow, 2)
gridRow = gridRow + 1
- self.patternOptionsCheckBoxLayout.addWidget(self.useUnicodePropertiesOptionCheckBox, gridRow,\
- 1)
+ self.patternOptionsCheckBoxLayout.addWidget(
+ self.useUnicodePropertiesOptionCheckBox, gridRow, 1)
form_layout.addRow("Pattern options:", self.patternOptionsCheckBoxLayout)
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
if not self.contacts:
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
if not self.contacts:
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if self._current_mode == self.AddingMode:
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._current_mode == self.EditingMode:
if self._old_name != name:
if name not in self.contacts:
QMessageBox.information(self, "Edit Successful",
- f'"{self.oldName}" has been edited in your address book.')
+ f'"{self.oldName}" has been edited in your '
+ 'address book.')
del self.contacts[self._old_name]
self.contacts[name] = address
else:
QMessageBox.information(self, "Edit Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._old_address != address:
QMessageBox.information(self, "Edit Successful",
- f'"{name}" has been edited in your address book.')
+ f'"{name}" has been edited in your address book.')
self.contacts[name] = address
self.update_interface(self.NavigationMode)
@Slot()
def remove_contact(self):
name = self._name_line.text()
- address = self._address_text.toPlainText()
if name in self.contacts:
button = QMessageBox.question(self, "Confirm Remove",
- f'Are you sure you want to remove "{name}"?',
- QMessageBox.Yes | QMessageBox.No)
+ f'Are you sure you want to remove "{name}"?',
+ QMessageBox.Yes | QMessageBox.No)
if button == QMessageBox.Yes:
self.previous()
del self.contacts[name]
QMessageBox.information(self, "Remove Successful",
- f'"{name}" has been removed from your address book.')
+ f'"{name}" has been removed from your address book.')
self.update_interface(self.NavigationMode)
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if self._current_mode == self.AddingMode:
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._current_mode == self.EditingMode:
if self._old_name != name:
if name not in self.contacts:
QMessageBox.information(self, "Edit Successful",
- f'"{self.oldName}" has been edited in your address book.')
+ f'"{self.oldName}" has been edited in your '
+ 'address book.')
del self.contacts[self._old_name]
self.contacts[name] = address
else:
QMessageBox.information(self, "Edit Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._old_address != address:
QMessageBox.information(self, "Edit Successful",
- f'"{name}" has been edited in your address book.')
+ f'"{name}" has been edited in your address book.')
self.contacts[name] = address
self.update_interface(self.NavigationMode)
@Slot()
def remove_contact(self):
name = self._name_line.text()
- address = self._address_text.toPlainText()
if name in self.contacts:
button = QMessageBox.question(self, "Confirm Remove",
- f'Are you sure you want to remove "{name}"?',
- QMessageBox.Yes | QMessageBox.No)
+ f'Are you sure you want to remove "{name}"?',
+ QMessageBox.Yes | QMessageBox.No)
if button == QMessageBox.Yes:
self.previous()
del self.contacts[name]
QMessageBox.information(self, "Remove Successful",
- f'"{name}" has been removed from your address book.')
+ f'"{name}" has been removed from your address book.')
self.update_interface(self.NavigationMode)
self._address_text.setText(self.contacts[contact_name])
else:
QMessageBox.information(self, "Contact Not Found",
- f'Sorry, "{contact_name}" is not in your address book.')
+ f'Sorry, "{contact_name}" is not in your address book.')
return
self.update_interface(self.NavigationMode)
text = self._line_edit.text()
if not text:
- QMessageBox.information(self, "Empty Field",
- "Please enter a name.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name.")
return
else:
self._find_text = text
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if self._current_mode == self.AddingMode:
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._current_mode == self.EditingMode:
if self._old_name != name:
if name not in self.contacts:
QMessageBox.information(self, "Edit Successful",
- f'"{self.oldName}" has been edited in your address book.')
+ f'"{self.oldName}" has been edited in your '
+ 'address book.')
del self.contacts[self._old_name]
self.contacts[name] = address
else:
QMessageBox.information(self, "Edit Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._old_address != address:
QMessageBox.information(self, "Edit Successful",
- f'"{name}" has been edited in your address book.')
+ f'"{name}" has been edited in your address book.')
self.contacts[name] = address
self.update_interface(self.NavigationMode)
@Slot()
def remove_contact(self):
name = self._name_line.text()
- address = self._address_text.toPlainText()
if name in self.contacts:
button = QMessageBox.question(self, "Confirm Remove",
- f'Are you sure you want to remove "{name}"?',
- QMessageBox.Yes | QMessageBox.No)
+ f'Are you sure you want to remove "{name}"?',
+ QMessageBox.Yes | QMessageBox.No)
if button == QMessageBox.Yes:
self.previous()
del self.contacts[name]
QMessageBox.information(self, "Remove Successful",
- f'"{name}" has been removed from your address book.')
+ f'"{name}" has been removed from your address book.')
self.update_interface(self.NavigationMode)
self._address_text.setText(self.contacts[contact_name])
else:
QMessageBox.information(self, "Contact Not Found",
- f'Sorry, "{contact_name}" is not in your address book.')
+ f'Sorry, "{contact_name}" is not in your address book.')
return
self.update_interface(self.NavigationMode)
def save_to_file(self):
fileName, _ = QFileDialog.getSaveFileName(self,
- "Save Address Book", '',
- "Address Book (*.abk);;All Files (*)")
+ "Save Address Book", '',
+ "Address Book (*.abk);;All Files (*)")
if not fileName:
return
out_file = open(str(fileName), 'wb')
except IOError:
QMessageBox.information(self, "Unable to open file",
- f'There was an error opening "{fileName}"')
+ f'There was an error opening "{fileName}"')
return
pickle.dump(self.contacts, out_file)
def load_from_file(self):
fileName, _ = QFileDialog.getOpenFileName(self,
- "Open Address Book", '',
- "Address Book (*.abk);;All Files (*)")
+ "Open Address Book", '',
+ "Address Book (*.abk);;All Files (*)")
if not fileName:
return
in_file = open(str(fileName), 'rb')
except IOError:
QMessageBox.information(self, "Unable to open file",
- f'There was an error opening "{fileName}"')
+ f'There was an error opening "{fileName}"')
return
self.contacts = pickle.load(in_file)
if len(self.contacts) == 0:
QMessageBox.information(self, "No contacts in file",
- "The file you are attempting to open contains no "
- "contacts.")
+ "The file you are attempting to open contains no contacts.")
else:
for name, address in self.contacts:
self._name_line.setText(name)
text = self._line_edit.text()
if not text:
- QMessageBox.information(self, "Empty Field",
- "Please enter a name.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name.")
return
self._find_text = text
address = self._address_text.toPlainText()
if name == "" or address == "":
- QMessageBox.information(self, "Empty Field",
- "Please enter a name and address.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
return
if self._current_mode == self.AddingMode:
if name not in self.contacts:
self.contacts[name] = address
QMessageBox.information(self, "Add Successful",
- f'"{name}" has been added to your address book.')
+ f'"{name}" has been added to your address book.')
else:
QMessageBox.information(self, "Add Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._current_mode == self.EditingMode:
if self._old_name != name:
if name not in self.contacts:
QMessageBox.information(self, "Edit Successful",
- f'"{self.oldName}" has been edited in your address book.')
+ f'"{self.oldName}" has been edited in your '
+ 'address book.')
del self.contacts[self._old_name]
self.contacts[name] = address
else:
QMessageBox.information(self, "Edit Unsuccessful",
- f'Sorry, "{name}" is already in your address book.')
+ f'Sorry, "{name}" is already in your address book.')
return
elif self._old_address != address:
QMessageBox.information(self, "Edit Successful",
- f'"{name}" has been edited in your address book.')
+ f'"{name}" has been edited in your address book.')
self.contacts[name] = address
self.update_interface(self.NavigationMode)
@Slot()
def remove_contact(self):
name = self._name_line.text()
- address = self._address_text.toPlainText()
if name in self.contacts:
button = QMessageBox.question(self, "Confirm Remove",
- f'Are you sure you want to remove "{name}"?',
- QMessageBox.Yes | QMessageBox.No)
+ f'Are you sure you want to remove "{name}"?',
+ QMessageBox.Yes | QMessageBox.No)
if button == QMessageBox.Yes:
self.previous()
del self.contacts[name]
QMessageBox.information(self, "Remove Successful",
- f'"{name}" has been removed from your address book.')
+ f'"{name}" has been removed from your address book.')
self.update_interface(self.NavigationMode)
self._address_text.setText(self.contacts[contact_name])
else:
QMessageBox.information(self, "Contact Not Found",
- f'Sorry, "{contact_name}" is not in your address book.')
+ f'Sorry, "{contact_name}" is not in your address book.')
return
self.update_interface(self.NavigationMode)
def save_to_file(self):
fileName, _ = QFileDialog.getSaveFileName(self,
- "Save Address Book", '',
- "Address Book (*.abk);;All Files (*)")
+ "Save Address Book", '',
+ "Address Book (*.abk);;All Files (*)")
if not fileName:
return
out_file = open(str(fileName), 'wb')
except IOError:
QMessageBox.information(self, "Unable to open file",
- f'There was an error opening "{fileName}"')
+ f'There was an error opening "{fileName}"')
return
pickle.dump(self.contacts, out_file)
def load_from_file(self):
fileName, _ = QFileDialog.getOpenFileName(self,
- "Open Address Book", '',
- "Address Book (*.abk);;All Files (*)")
+ "Open Address Book", '',
+ "Address Book (*.abk);;All Files (*)")
if not fileName:
return
in_file = open(str(fileName), 'rb')
except IOError:
QMessageBox.information(self, "Unable to open file",
- f'There was an error opening "{fileName}"')
+ f'There was an error opening "{fileName}"')
return
self.contacts = pickle.load(in_file)
if len(self.contacts) == 0:
QMessageBox.information(self, "No contacts in file",
- "The file you are attempting to open contains no "
- "contacts.")
+ "The file you are attempting to open contains no contacts.")
else:
for name, address in self.contacts:
self._name_line.setText(name)
last_name = ''
file_name = QFileDialog.getSaveFileName(self, "Export Contact",
- '', "vCard Files (*.vcf);;All Files (*)")[0]
+ '', "vCard Files (*.vcf);;All Files (*)")[0]
if not file_name:
return
out_file = QFile(file_name)
if not out_file.open(QIODevice.WriteOnly):
- QMessageBox.information(self, "Unable to open file",
- out_file.errorString())
+ QMessageBox.information(self, "Unable to open file", out_file.errorString())
return
out_s = QTextStream(out_file)
out_s << 'END:VCARD' << '\n'
QMessageBox.information(self, "Export Successful",
- f'"{name}" has been exported as a vCard.')
+ f'"{name}" has been exported as a vCard.')
class FindDialog(QDialog):
text = self._line_edit.text()
if not text:
- QMessageBox.information(self, "Empty Field",
- "Please enter a name.")
+ QMessageBox.information(self, "Empty Field", "Please enter a name.")
return
self._find_text = text
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("&Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange()
angle.set_range(5, 70)
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("&Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange()
angle.set_range(5, 70)
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("&Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange("ANGLE")
angle.set_range(5, 70)
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("&Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange("ANGLE")
angle.set_range(5, 70)
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
self._auto_shoot_timer.stop()
self.hit.emit()
self.can_shoot.emit(True)
- elif shot_r.x() > self.width() or shot_r.y() > self.height() or shot_r.intersects(self.barrier_rect()):
+ elif (shot_r.x() > self.width() or shot_r.y() > self.height()
+ or shot_r.intersects(self.barrier_rect())):
self._auto_shoot_timer.stop()
self.missed.emit()
self.can_shoot.emit(True)
quit = QPushButton("&Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange("ANGLE")
angle.set_range(5, 70)
self.quit.setGeometry(62, 40, 75, 30)
self.quit.setFont(QFont("Times", 18, QFont.Bold))
- self.quit.clicked.connect(qApp.quit)
+ self.quit.clicked.connect(qApp.quit) # noqa: F821
if __name__ == '__main__':
slider.setRange(0, 99)
slider.setValue(0)
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
slider.valueChanged.connect(lcd.display)
layout = QVBoxLayout(self)
quit = QPushButton("Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
layout = QVBoxLayout(self)
layout.addWidget(quit)
quit = QPushButton("Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
previous_range = None
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning("LCDRange.setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange()
angle.set_range(5, 70)
def set_range(self, minValue, maxValue):
if minValue < 0 or maxValue > 99 or minValue > maxValue:
qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
- "\tRange must be 0..99\n"
- "\tand minValue must not be greater than maxValue")
+ "\tRange must be 0..99\n"
+ "\tand minValue must not be greater than maxValue")
return
self.slider.setRange(minValue, maxValue)
quit = QPushButton("Quit")
quit.setFont(QFont("Times", 18, QFont.Bold))
- quit.clicked.connect(qApp.quit)
+ quit.clicked.connect(qApp.quit) # noqa: F821
angle = LCDRange()
angle.set_range(5, 70)
return "<--left"
if row == 1 and col == 1:
return "right-->"
- return f"Row{row}, Column{col+1}"
+ return f"Row{row}, Column{col + 1}"
elif role == Qt.FontRole:
if row == 0 and col == 0: # change font only for cell(0,0)
self.dataChanged.emit(top_left, top_left, [Qt.DisplayRole])
#! [3]
+
if __name__ == '__main__':
app = QApplication(sys.argv)
table_view = QTableView()
"""PySide6 port of the widgets/tutorials/modelview/6_treeview example from Qt v6.x"""
+
#! [1]
class MainWindow(QMainWindow):
def __init__(self, parent=None):
"""PySide6 port of the widgets/tutorials/modelview/7_selections example from Qt v6.x"""
+
#! [1]
class MainWindow(QMainWindow):
def __init__(self, parent=None):
self._square_size, self._square_size, QBrush(Qt.red))
text = chr(key)
- painter.drawText(column * self._square_size + (self._square_size / 2) -
- font_metrics.horizontalAdvance(text) / 2,
+ painter.drawText(column * self._square_size + (self._square_size / 2)
+ - font_metrics.horizontalAdvance(text) / 2,
row * self._square_size + 4 + font_metrics.ascent(),
text)
file_menu.addAction("Quit", self.close)
help_menu = self.menuBar().addMenu("Help")
help_menu.addAction("Show Font Info", self.show_info)
- help_menu.addAction("About &Qt", qApp.aboutQt)
+ help_menu.addAction("About &Qt", qApp.aboutQt) # noqa: F821
central_widget = QWidget()
Digital Clock Example
=====================
+.. tags:: Android
+
The Digital Clock example shows how to use QLCDNumber to display a number with
LCD-like digits.
start_button.clicked.connect(self.board.start)
pause_button.clicked.connect(self.board.pause)
- quit_button.clicked.connect(qApp.quit)
+ quit_button.clicked.connect(qApp.quit) # noqa: F821
self.board.score_changed.connect(score_lcd.display)
self.board.level_changed.connect(level_lcd.display)
self.board.lines_removed_changed.connect(lines_lcd.display)
def sizeHint(self):
return QSize(TetrixBoard.board_width * 15 + self.frameWidth() * 2,
- TetrixBoard.board_height * 15 + self.frameWidth() * 2)
+ TetrixBoard.board_height * 15 + self.frameWidth() * 2)
def minimum_size_hint(self):
return QSize(TetrixBoard.board_width * 5 + self.frameWidth() * 2,
- TetrixBoard.board_height * 5 + self.frameWidth() * 2)
+ TetrixBoard.board_height * 5 + self.frameWidth() * 2)
@Slot()
def start(self):
shape = self.shape_at(j, TetrixBoard.board_height - i - 1)
if shape != Piece.NoShape:
self.draw_square(painter,
- rect.left() + j * self.square_width(),
- board_top + i * self.square_height(), shape)
+ rect.left() + j * self.square_width(),
+ board_top + i * self.square_height(), shape)
if self._cur_piece.shape() != Piece.NoShape:
for i in range(4):
x = self._cur_x + self._cur_piece.x(i)
y = self._cur_y - self._cur_piece.y(i)
self.draw_square(painter, rect.left() + x * self.square_width(),
- board_top + (TetrixBoard.board_height - y - 1) * self.square_height(),
- self._cur_piece.shape())
+ board_top
+ + (TetrixBoard.board_height - y - 1) * self.square_height(),
+ self._cur_piece.shape())
def keyPressEvent(self, event):
if not self._is_started or self._is_paused or self._cur_piece.shape() == Piece.NoShape:
super(TetrixBoard, self).timerEvent(event)
def clear_board(self):
- self.board = [Piece.NoShape for i in range(TetrixBoard.board_height * TetrixBoard.board_width)]
+ self.board = [
+ Piece.NoShape for _ in range(TetrixBoard.board_height * TetrixBoard.board_width)]
def drop_down(self):
drop_height = 0
x = self._next_piece.x(i) - self._next_piece.min_x()
y = self._next_piece.y(i) - self._next_piece.min_y()
self.draw_square(painter, x * self.square_width(),
- y * self.square_height(), self._next_piece.shape())
+ y * self.square_height(), self._next_piece.shape())
self.nextPieceLabel.setPixmap(pixmap)
def draw_square(self, painter, x, y, shape):
color_table = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
- 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
+ 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
color = QColor(color_table[shape])
- painter.fillRect(x + 1, y + 1, self.square_width() - 2,
- self.square_height() - 2, color)
+ painter.fillRect(x + 1, y + 1, self.square_width() - 2, self.square_height() - 2, color)
painter.setPen(color.lighter())
painter.drawLine(x, y + self.square_height() - 1, x, y)
painter.setPen(color.darker())
painter.drawLine(x + 1, y + self.square_height() - 1,
- x + self.square_width() - 1, y + self.square_height() - 1)
+ x + self.square_width() - 1, y + self.square_height() - 1)
painter.drawLine(x + self.square_width() - 1,
- y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
+ y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
class TetrixPiece(object):
coords_table = (
- ((0, 0), (0, 0), (0, 0), (0, 0)),
- ((0, -1), (0, 0), (-1, 0), (-1, 1)),
- ((0, -1), (0, 0), (1, 0), (1, 1)),
- ((0, -1), (0, 0), (0, 1), (0, 2)),
- ((-1, 0), (0, 0), (1, 0), (0, 1)),
- ((0, 0), (1, 0), (0, 1), (1, 1)),
- ((-1, -1), (0, -1), (0, 0), (0, 1)),
- ((1, -1), (0, -1), (0, 0), (0, 1))
+ ((0, 0), (0, 0), (0, 0), (0, 0)),
+ ((0, -1), (0, 0), (-1, 0), (-1, 1)),
+ ((0, -1), (0, 0), (1, 0), (1, 1)),
+ ((0, -1), (0, 0), (0, 1), (0, 2)),
+ ((-1, 0), (0, 0), (1, 0), (0, 1)),
+ ((0, 0), (1, 0), (0, 1), (1, 1)),
+ ((-1, -1), (0, -1), (0, 0), (0, 1)),
+ ((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
- self.coords = [[0,0] for _ in range(4)]
+ self.coords = [[0, 0] for _ in range(4)]
self._piece_shape = Piece.NoShape
self.set_shape(Piece.NoShape)
top_layout.addStretch(1)
top_layout.addWidget(disable_widgets_checkbox)
- dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help |
- QDialogButtonBox.Close)
+ dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help
+ | QDialogButtonBox.Close)
init_widget(dialog_buttonbox, "dialogButtonBox")
dialog_buttonbox.helpRequested.connect(launch_module_help)
dialog_buttonbox.rejected.connect(self.reject)
import sys
from PySide6.QtCore import QDir, QFile, Qt, QTextStream
-from PySide6.QtGui import QAction, QIcon
+from PySide6.QtGui import QAction, QIcon, QKeySequence
from PySide6.QtWidgets import (QApplication, QFileDialog, QHeaderView,
QMainWindow, QMessageBox, QStyle, QTreeWidget,
QTreeWidgetItem)
def open(self):
file_name = QFileDialog.getOpenFileName(self,
- "Open Bookmark File", QDir.currentPath(),
- "XBEL Files (*.xbel *.xml)")[0]
+ "Open Bookmark File", QDir.currentPath(),
+ "XBEL Files (*.xbel *.xml)")[0]
if not file_name:
return
if not in_file.open(QFile.ReadOnly | QFile.Text):
reason = in_file.errorString()
QMessageBox.warning(self, "DOM Bookmarks",
- f"Cannot read file {file_name}:\n{reason}.")
+ f"Cannot read file {file_name}:\n{reason}.")
return
if self._xbel_tree.read(in_file):
def save_as(self):
file_name = QFileDialog.getSaveFileName(self,
- "Save Bookmark File", QDir.currentPath(),
- "XBEL Files (*.xbel *.xml)")[0]
+ "Save Bookmark File", QDir.currentPath(),
+ "XBEL Files (*.xbel *.xml)")[0]
if not file_name:
return
if not out_file.open(QFile.WriteOnly | QFile.Text):
reason = out_file.errorString()
QMessageBox.warning(self, "DOM Bookmarks",
- "Cannot write file {fileName}:\n{reason}.")
+ f"Cannot write file {file_name}:\n{reason}.")
return
if self._xbel_tree.write(out_file):
def about(self):
QMessageBox.about(self, "About DOM Bookmarks",
- "The <b>DOM Bookmarks</b> example demonstrates how to use Qt's "
- "DOM classes to read and write XML documents.")
+ "The <b>DOM Bookmarks</b> example demonstrates how to use Qt's "
+ "DOM classes to read and write XML documents.")
def create_menus(self):
self._file_menu = self.menuBar().addMenu("&File")
self._file_menu.addAction(QAction("&Open...", self,
- shortcut=QKeySequence(Qt.CTRL | Qt.Key_O), triggered=self.open))
+ shortcut=QKeySequence(
+ Qt.CTRL | Qt.Key_O), triggered=self.open))
self._file_menu.addAction(QAction("&Save As...", self,
- shortcut=QKeySequence(Qt.CTRL | Qt.Key_S), triggered=self.save_as))
+ shortcut=QKeySequence(
+ Qt.CTRL | Qt.Key_S), triggered=self.save_as))
self._file_menu.addAction(QAction("E&xit", self,
- shortcut=QKeySequence(Qt.CTRL | Qt.Key_Q), triggered=self.close))
+ shortcut=QKeySequence(
+ Qt.CTRL | Qt.Key_Q), triggered=self.close))
self.menuBar().addSeparator()
self._help_menu = self.menuBar().addMenu("&Help")
- self._help_menu.addAction(QAction("&About", self,
- triggered=self.about))
- self._help_menu.addAction(QAction("About &Qt", self,
- triggered=qApp.aboutQt))
+ self._help_menu.addAction(QAction("&About", self, triggered=self.about))
+ self._help_menu.addAction(QAction("About &Qt", self, triggered=qApp.aboutQt)) # noqa: F821
class XbelTree(QTreeWidget):
self._bookmark_icon = QIcon()
self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon),
- QIcon.Normal, QIcon.Off)
+ QIcon.Normal, QIcon.Off)
self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon),
- QIcon.Normal, QIcon.On)
+ QIcon.Normal, QIcon.On)
self._bookmark_icon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon))
def read(self, device):
ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True)
if not ok:
QMessageBox.information(self.window(), "DOM Bookmarks",
- f"Parse error at line {errorLine}, column {errorColumn}:\n{errorStr}")
+ f"Parse error at line {errorLine}, "
+ f"column {errorColumn}:\n{errorStr}")
return False
root = self._dom_document.documentElement()
if root.tagName() != 'xbel':
QMessageBox.information(self.window(), "DOM Bookmarks",
- "The file is not an XBEL file.")
+ "The file is not an XBEL file.")
return False
elif root.hasAttribute('version') and root.attribute('version') != '1.0':
QMessageBox.information(self.window(), "DOM Bookmarks",
- "The file is not an XBEL version 1.0 file.")
+ "The file is not an XBEL version 1.0 file.")
return False
self.clear()
sphinx==7.2.6
sphinx-design==0.5.0
sphinx-copybutton==0.5.2
+sphinx-tags==0.3.1
myst-parser==2.0.0
-furo==2023.9.10
+# FIXME: Using fork in order to enable the 'collapse_navbar=True'
+# option for the sphinx-theme. Upstream proposal:
+# https://github.com/pradyunsg/furo/pull/748#issuecomment-1895448722
+# furo==2023.9.10
+furo @ git+https://github.com/cmaureir/furo@add_collapse
graphviz==0.20
-setuptools==67.8.0
-packaging
-build==0.7
-six
-wheel>=0.35
+# Build dependencies
+setuptools==69.0.3
+packaging==23.2
+build==1.0.3
+wheel==0.42.0
+distro==1.9.0; sys_platform == 'linux'
+patchelf==0.17.2; sys_platform == 'linux'
+# 1.24.4 is the last version that supports Python 3.8
+numpy<1.25; python_version < '3.9'
+numpy==1.26.3; python_version >= '3.9'
+
+# For examples
PyOpenGL
+
+# For tests
pyinstaller==3.6; platform_machine != 'aarch64'
-numpy
-nuitka==1.4.8; platform_machine != 'aarch64'
-distro; sys_platform == 'linux'
-patchelf==0.15; sys_platform == 'linux'
-pkginfo; sys_platform == 'linux'
-jinja2; sys_platform == 'linux'
-buildozer==1.5.0; sys_platform == 'linux'
-tqdm; sys_platform == 'linux'
-gitpython; sys_platform == 'linux'
${CMAKE_CURRENT_SOURCE_DIR}/qml.py
${CMAKE_CURRENT_SOURCE_DIR}/qtpy2cpp.py
${CMAKE_CURRENT_SOURCE_DIR}/deploy.py
- ${CMAKE_CURRENT_SOURCE_DIR}/android_deploy.py)
+ ${CMAKE_CURRENT_SOURCE_DIR}/android_deploy.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/requirements-android.txt)
set(directories ${CMAKE_CURRENT_SOURCE_DIR}/deploy_lib
${CMAKE_CURRENT_SOURCE_DIR}/project)
from pathlib import Path
from textwrap import dedent
-from deploy_lib import (MAJOR_VERSION, cleanup, config_option_exists,
- find_pyside_modules, get_config,
- install_python_dependencies, setup_python)
-from deploy_lib.android import (AndroidData, Buildozer, extract_and_copy_jar,
- get_wheel_android_arch)
+from deploy_lib import (setup_python, create_config_file, cleanup, install_python_dependencies,
+ config_option_exists, MAJOR_VERSION)
+from deploy_lib.android import AndroidData, AndroidConfig
+from deploy_lib.android.buildozer import Buildozer
+
""" pyside6-android-deploy deployment tool
""")
-def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = None, ndk_path: Path = None,
- sdk_path: Path = None, config_file: Path = None, init: bool = False,
+def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = None,
+ ndk_path: Path = None, sdk_path: Path = None, config_file: Path = None, init: bool = False,
loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False,
force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None):
extra_modules.append(extra_module)
main_file = Path.cwd() / "main.py"
- generated_files_path = None
if not main_file.exists():
raise RuntimeError(("[DEPLOY] For Android deployment to work, the main"
" entrypoint Python file should be named 'main.py'"
ndk_path=ndk_path, sdk_path=sdk_path)
python = setup_python(dry_run=dry_run, force=force, init=init)
- config = get_config(python_exe=python.exe, dry_run=dry_run, config_file=config_file,
- main_file=main_file, android_data=android_data, is_android=True)
+
+ config_file_exists = config_file and Path(config_file).exists()
+
+ if config_file_exists:
+ logging.info(f"[DEPLOY] Using existing config file {config_file}")
+ else:
+ config_file = create_config_file(dry_run=dry_run, config_file=config_file,
+ main_file=main_file)
+
+ config = AndroidConfig(config_file=config_file, source_file=main_file,
+ python_exe=python.exe, dry_run=dry_run, android_data=android_data,
+ existing_config_file=config_file_exists,
+ extra_ignore_dirs=extra_ignore_dirs)
if not config.wheel_pyside and not config.wheel_shiboken:
raise RuntimeError(f"[DEPLOY] No PySide{MAJOR_VERSION} and Shiboken{MAJOR_VERSION} wheels"
"found")
- source_file = config.project_dir / config.source_file
- generated_files_path = source_file.parent / "deployment"
- cleanup(generated_files_path=generated_files_path, config=config, is_android=True)
+ cleanup(config=config, is_android=True)
install_python_dependencies(config=config, python=python, init=init,
packages="android_packages", is_android=True)
config.title = name
try:
- # check which modules are needed
- if not config.modules:
- config.modules = find_pyside_modules(project_dir=config.project_dir,
- extra_ignore_dirs=extra_ignore_dirs,
- project_data=config.project_data)
- logging.info("The following PySide modules were found from the python files of "
- f"the project {config.modules}")
- config.modules.extend(extra_modules)
-
- # extract out and copy .jar files to {generated_files_path}
- if not config.jars_dir or not Path(config.jars_dir).exists() and not dry_run:
- logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
- f"{generated_files_path}")
- config.jars_dir = extract_and_copy_jar(wheel_path=config.wheel_pyside,
- generated_files_path=generated_files_path)
-
- # find architecture from wheel name
- if not config.arch:
- arch = get_wheel_android_arch(wheel=config.wheel_pyside)
- if not arch:
- raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
- "platform name")
- config.arch = arch
+ config.modules += extra_modules
+
+ # this cannot be done when config file is initialized because cleanup() removes it
+ # so this can only be done after the cleanup()
+ config.find_and_set_jars_dir()
# TODO: include qml files from pysidedeploy.spec rather than from extensions
# buildozer currently includes all the files with .qml extension
# init buildozer
Buildozer.dry_run = dry_run
logging.info("[DEPLOY] Creating buildozer.spec file")
- Buildozer.initialize(pysidedeploy_config=config, generated_files_path=generated_files_path)
+ Buildozer.initialize(pysidedeploy_config=config)
# writing config file
if not dry_run:
buildozer_build_dir = config.project_dir / ".buildozer"
if not buildozer_build_dir.exists():
logging.info(f"[DEPLOY] Unable to copy {buildozer_build_dir} to "
- f"{generated_files_path}. {buildozer_build_dir} does not exist")
- logging.info(f"[DEPLOY] Copying {str(buildozer_build_dir)} to "
- f"{str(generated_files_path)}")
- shutil.move(buildozer_build_dir, generated_files_path)
+ f"{config.generated_files_path}. {buildozer_build_dir} does not exist")
+ logging.info(f"[DEPLOY] copy {buildozer_build_dir} to {config.generated_files_path}")
+ shutil.move(buildozer_build_dir, config.generated_files_path)
logging.info(f"[DEPLOY] apk created in {config.exe_dir}")
except Exception:
print(f"Exception occurred: {traceback.format_exc()}")
finally:
- if generated_files_path and config and not keep_deployment_files:
- cleanup(generated_files_path=generated_files_path, config=config, is_android=True)
+ if config.generated_files_path and config and not keep_deployment_files:
+ cleanup(config=config, is_android=True)
logging.info("[DEPLOY] End")
)
parser.add_argument("-c", "--config-file", type=lambda p: Path(p).absolute(),
+ default=(Path.cwd() / "pysidedeploy.spec"),
help="Path to the .spec config file")
parser.add_argument(
from textwrap import dedent
from deploy_lib import (MAJOR_VERSION, Config, cleanup, config_option_exists,
- finalize, get_config, install_python_dependencies,
+ finalize, create_config_file, install_python_dependencies,
setup_python)
# Nuitka command to run
command_str = None
- generated_files_path = None
config = None
logging.info("[DEPLOY] Start")
python = setup_python(dry_run=dry_run, force=force, init=init)
- config = get_config(python_exe=python.exe, dry_run=dry_run, config_file=config_file,
- main_file=main_file)
+ config_file_exists = config_file and Path(config_file).exists()
+
+ if config_file_exists:
+ logging.info(f"[DEPLOY] Using existing config file {config_file}")
+ else:
+ config_file = create_config_file(dry_run=dry_run, config_file=config_file,
+ main_file=main_file)
+
+ config = Config(config_file=config_file, source_file=main_file, python_exe=python.exe,
+ dry_run=dry_run, existing_config_file=config_file_exists)
# set application name
if name:
config.title = name
- source_file = config.project_dir / config.source_file
-
- generated_files_path = source_file.parent / "deployment"
- cleanup(generated_files_path=generated_files_path, config=config)
+ cleanup(config=config)
install_python_dependencies(config=config, python=python, init=init,
packages="packages")
# required by Nuitka for pyenv Python
- if python.is_pyenv_python():
- config.extra_args += " --static-libpython=no"
+ add_arg = " --static-libpython=no"
+ if python.is_pyenv_python() and add_arg not in config.extra_args:
+ config.extra_args += add_arg
# writing config file
# in the case of --dry-run, we use default.spec as reference. Do not save the changes
if not dry_run:
logging.info("[DEPLOY] Deploying application")
- command_str = python.create_executable(
- source_file=source_file,
- extra_args=config.extra_args,
- config=config,
- )
+ command_str = python.create_executable(source_file=config.source_file,
+ extra_args=config.extra_args,
+ config=config)
except Exception:
print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}")
finally:
- if generated_files_path and config:
- finalize(generated_files_path=generated_files_path, config=config)
+ if config.generated_files_path and config:
+ finalize(config=config)
if not keep_deployment_files:
- cleanup(generated_files_path=generated_files_path, config=Config)
+ cleanup(config=config)
logging.info("[DEPLOY] End")
return command_str
{
"files": ["deploy.py", "deploy_lib/__init__.py", "deploy_lib/commands.py", "deploy_lib/config.py",
- "deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.jpg",
+ "deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.ico",
+ "deploy_lib/pyside_icon.icns","deploy_lib/pyside_icon.jpg",
"deploy_lib/python_helper.py", "deploy_lib/deploy_util.py"
]
}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import sys
+from pathlib import Path
MAJOR_VERSION = 6
-EXE_FORMAT = ".exe" if sys.platform == "win32" else ".bin"
+
+if sys.platform == "win32":
+ IMAGE_FORMAT = ".ico"
+ EXE_FORMAT = ".exe"
+elif sys.platform == "darwin":
+ IMAGE_FORMAT = ".icns"
+ EXE_FORMAT = ".bin"
+else:
+ IMAGE_FORMAT = ".jpg"
+ EXE_FORMAT = ".bin"
+
+DEFAULT_APP_ICON = str((Path(__file__).parent / f"pyside_icon{IMAGE_FORMAT}").resolve())
from .commands import run_command
-from .config import BaseConfig, Config
from .nuitka_helper import Nuitka
-from .deploy_util import (cleanup, config_option_exists, finalize, get_config,
- install_python_dependencies, setup_python)
from .python_helper import PythonExecutable, find_pyside_modules
+from .config import BaseConfig, Config
+from .deploy_util import (cleanup, finalize, create_config_file, setup_python,
+ install_python_dependencies, config_option_exists)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-from .android_helper import (AndroidData, create_recipe, extract_and_copy_jar,
- find_lib_dependencies, find_qtlibs_in_wheel,
- get_llvm_readobj, get_wheel_android_arch)
-from .buildozer import Buildozer
+from .android_helper import (create_recipe, extract_and_copy_jar,
+ get_wheel_android_arch, AndroidData, get_llvm_readobj,
+ find_lib_dependencies, find_qtlibs_in_wheel)
+from .android_config import AndroidConfig
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import logging
+
+from typing import List
+from pathlib import Path
+
+from . import extract_and_copy_jar, get_wheel_android_arch
+from .. import Config, find_pyside_modules
+
+ANDROID_NDK_VERSION = "25c"
+ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+
+
+class AndroidConfig(Config):
+ """
+ Wrapper class around pysidedeploy.spec file for pyside6-android-deploy
+ """
+ def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
+ android_data, existing_config_file: bool = False,
+ extra_ignore_dirs: List[str] = None):
+ super().__init__(config_file=config_file, source_file=source_file, python_exe=python_exe,
+ dry_run=dry_run, existing_config_file=existing_config_file)
+
+ self.extra_ignore_dirs = extra_ignore_dirs
+
+ if android_data.wheel_pyside:
+ self.wheel_pyside = android_data.wheel_pyside
+ else:
+ wheel_pyside_temp = self.get_value("android", "wheel_pyside")
+ if not wheel_pyside_temp:
+ raise RuntimeError("[DEPLOY] Unable to find PySide6 Android wheel")
+ self.wheel_pyside = Path(wheel_pyside_temp).resolve()
+
+ if android_data.wheel_shiboken:
+ self.wheel_shiboken = android_data.wheel_shiboken
+ else:
+ wheel_shiboken_temp = self.get_value("android", "wheel_shiboken")
+ if not wheel_shiboken_temp:
+ raise RuntimeError("[DEPLOY] Unable to find shiboken6 Android wheel")
+ self.wheel_shiboken = Path(wheel_shiboken_temp).resolve()
+
+ self.ndk_path = None
+ if android_data.ndk_path:
+ # from cli
+ self.ndk_path = android_data.ndk_path
+ else:
+ # from config
+ ndk_path_temp = self.get_value("buildozer", "ndk_path")
+ if ndk_path_temp:
+ self.ndk_path = Path(ndk_path_temp)
+ else:
+ ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
+ / f"android-ndk-r{ANDROID_NDK_VERSION}")
+ if ndk_path_temp.exists():
+ self.ndk_path = ndk_path_temp
+
+ if self.ndk_path:
+ print(f"Using Android NDK: {str(self.ndk_path)}")
+ else:
+ raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
+ "path either from the CLI or from pysidedeploy.spec")
+
+ self.sdk_path = None
+ if android_data.sdk_path:
+ self.sdk_path = android_data.sdk_path
+ else:
+ sdk_path_temp = self.get_value("buildozer", "sdk_path")
+ if sdk_path_temp:
+ self.sdk_path = Path(sdk_path_temp)
+ else:
+ sdk_path_temp = ANDROID_DEPLOY_CACHE / "android-sdk"
+ if sdk_path_temp.exists():
+ self.sdk_path = sdk_path_temp
+ else:
+ logging.info("[DEPLOY] Use default SDK from buildozer")
+
+ if self.sdk_path:
+ print(f"Using Android SDK: {str(self.sdk_path)}")
+
+ recipe_dir_temp = self.get_value("buildozer", "recipe_dir")
+ self.recipe_dir = Path(recipe_dir_temp) if recipe_dir_temp else None
+
+ self._jars_dir = []
+ jars_dir_temp = self.get_value("buildozer", "jars_dir")
+ if jars_dir_temp and Path(jars_dir_temp).resolve().exists():
+ self.jars_dir = Path(jars_dir_temp).resolve()
+
+ self._modules = []
+ if self.get_value("buildozer", "modules"):
+ self.modules = self.get_value("buildozer", "modules").split(",")
+ else:
+ self._find_and_set_pysidemodules()
+ self._find_and_set_qtquick_modules()
+
+ self._arch = None
+ if self.get_value("buildozer", "arch"):
+ self.arch = self.get_value("buildozer", "arch")
+ else:
+ self._find_and_set_arch()
+
+ self._local_libs = []
+ if self.get_value("buildozer", "local_libs"):
+ self.local_libs = self.get_value("buildozer", "local_libs").split(",")
+
+ self._qt_plugins = []
+ if self.get_value("android", "plugins"):
+ self._qt_plugins = self.get_value("android", "plugins").split(",")
+
+ self._mode = self.get_value("buildozer", "mode")
+
+ @property
+ def qt_plugins(self):
+ return self._qt_plugins
+
+ @qt_plugins.setter
+ def qt_plugins(self, qt_plugins):
+ self._qt_plugins = qt_plugins
+ self.set_value("android", "plugins", ",".join(qt_plugins))
+
+ @property
+ def ndk_path(self):
+ return self._ndk_path
+
+ @ndk_path.setter
+ def ndk_path(self, ndk_path: Path):
+ self._ndk_path = ndk_path.resolve() if ndk_path else None
+ if self._ndk_path:
+ self.set_value("buildozer", "ndk_path", str(self._ndk_path))
+
+ @property
+ def sdk_path(self) -> Path:
+ return self._sdk_path
+
+ @sdk_path.setter
+ def sdk_path(self, sdk_path: Path):
+ self._sdk_path = sdk_path.resolve() if sdk_path else None
+ if self._sdk_path:
+ self.set_value("buildozer", "sdk_path", str(self._sdk_path))
+
+ @property
+ def arch(self):
+ return self._arch
+
+ @arch.setter
+ def arch(self, arch):
+ self._arch = arch
+ self.set_value("buildozer", "arch", arch)
+
+ @property
+ def mode(self):
+ return self._mode
+
+ @property
+ def modules(self):
+ return self._modules
+
+ @modules.setter
+ def modules(self, modules):
+ self._modules = modules
+ self.set_value("buildozer", "modules", ",".join(modules))
+
+ @property
+ def local_libs(self):
+ return self._local_libs
+
+ @local_libs.setter
+ def local_libs(self, local_libs):
+ self._local_libs = local_libs
+ self.set_value("buildozer", "local_libs", ",".join(local_libs))
+
+ @property
+ def recipe_dir(self):
+ return self._recipe_dir
+
+ @recipe_dir.setter
+ def recipe_dir(self, recipe_dir: Path):
+ self._recipe_dir = recipe_dir.resolve() if recipe_dir else None
+ if self._recipe_dir:
+ self.set_value("buildozer", "recipe_dir", str(self._recipe_dir))
+
+ def recipes_exist(self):
+ if not self._recipe_dir:
+ return False
+
+ pyside_recipe_dir = Path(self.recipe_dir) / "PySide6"
+ shiboken_recipe_dir = Path(self.recipe_dir) / "shiboken6"
+
+ return pyside_recipe_dir.is_dir() and shiboken_recipe_dir.is_dir()
+
+ @property
+ def jars_dir(self) -> Path:
+ return self._jars_dir
+
+ @jars_dir.setter
+ def jars_dir(self, jars_dir: Path):
+ self._jars_dir = jars_dir.resolve() if jars_dir else None
+ if self._jars_dir:
+ self.set_value("buildozer", "jars_dir", str(self._jars_dir))
+
+ @property
+ def wheel_pyside(self) -> Path:
+ return self._wheel_pyside
+
+ @wheel_pyside.setter
+ def wheel_pyside(self, wheel_pyside: Path):
+ self._wheel_pyside = wheel_pyside.resolve() if wheel_pyside else None
+ if self._wheel_pyside:
+ self.set_value("android", "wheel_pyside", str(self._wheel_pyside))
+
+ @property
+ def wheel_shiboken(self) -> Path:
+ return self._wheel_shiboken
+
+ @wheel_shiboken.setter
+ def wheel_shiboken(self, wheel_shiboken: Path):
+ self._wheel_shiboken = wheel_shiboken.resolve() if wheel_shiboken else None
+ if self._wheel_shiboken:
+ self.set_value("android", "wheel_shiboken", str(self._wheel_shiboken))
+
+ def _find_and_set_pysidemodules(self):
+ self.modules = find_pyside_modules(project_dir=self.project_dir,
+ extra_ignore_dirs=self.extra_ignore_dirs,
+ project_data=self.project_data)
+ logging.info("The following PySide modules were found from the python files of "
+ f"the project {self.modules}")
+
+ def find_and_set_jars_dir(self):
+ """Extract out and copy .jar files to {generated_files_path}
+ """
+ if not self.dry_run:
+ logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
+ f"{self.generated_files_path}")
+ self.jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
+ generated_files_path=self.generated_files_path)
+
+ def _find_and_set_arch(self):
+ """Find architecture from wheel name
+ """
+ self.arch = get_wheel_android_arch(wheel=self.wheel_pyside)
+ if not self.arch:
+ raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
+ "platform name")
+
+ def _find_and_set_qtquick_modules(self):
+ """Identify if QtQuick is used in QML files and add them as dependency
+ """
+ extra_modules = []
+
+ if "QtQuick" in self.qml_modules:
+ extra_modules.append("Quick")
+
+ if "QtQuick.Controls" in self.qml_modules:
+ extra_modules.append("QuickControls2")
+
+ self.modules += extra_modules
qt_local_libs = []
if local_libs:
- qt_local_libs = [local_lib for local_lib in local_libs if local_lib.startswith("Qt6") ]
+ qt_local_libs = [local_lib for local_lib in local_libs if local_lib.startswith("Qt6")]
rcp_tmpl_path = Path(__file__).parent / "recipes" / f"{component}"
environment = Environment(loader=FileSystemLoader(rcp_tmpl_path))
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import sys
import logging
import re
import tempfile
class BuildozerConfig(BaseConfig):
- def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: Config,
- generated_files_path: Path):
+ def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: Config):
super().__init__(buildozer_spec_file, comment_prefixes="#")
self.set_value("app", "title", pysidedeploy_config.title)
self.set_value("app", "package.name", pysidedeploy_config.title)
self.set_value("app", "android.archs", self.arch)
# p4a changes
- logging.info("[DEPLOY] Using custom fork of python-for-android: "
- "https://github.com/shyamnathp/python-for-android/tree/pyside_support")
- self.set_value("app", "p4a.fork", "shyamnathp")
- self.set_value("app", "p4a.branch", "pyside_support_2")
self.set_value("app", "p4a.bootstrap", "qt")
self.qt_libs_path: zipfile.Path = (
extra_modules = self.__find_dependent_qt_modules(pysidedeploy_config)
logging.info(f"[DEPLOY] Other dependent modules to be added: {extra_modules}")
pysidedeploy_config.modules = pysidedeploy_config.modules + extra_modules
-
- # update the config file with the extra modules
- if extra_modules:
- pysidedeploy_config.update_config()
-
modules = ",".join(pysidedeploy_config.modules)
# gets the xml dependency files from Qt installation path
self.__find_plugin_dependencies(dependency_files, dependent_plugins)
pysidedeploy_config.qt_plugins += dependent_plugins
- if local_libs or dependent_plugins:
- pysidedeploy_config.update_config()
-
local_libs = ",".join(pysidedeploy_config.local_libs)
# create recipes
version = Wheel(pysidedeploy_config.wheel_pyside).version
create_recipe(version=version, component=f"PySide{MAJOR_VERSION}",
wheel_path=pysidedeploy_config.wheel_pyside,
- generated_files_path=generated_files_path,
+ generated_files_path=pysidedeploy_config.generated_files_path,
qt_modules=pysidedeploy_config.modules,
local_libs=pysidedeploy_config.local_libs,
plugins=pysidedeploy_config.qt_plugins)
create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
wheel_path=pysidedeploy_config.wheel_shiboken,
- generated_files_path=generated_files_path)
- pysidedeploy_config.recipe_dir = (generated_files_path / "recipes").resolve()
+ generated_files_path=pysidedeploy_config.generated_files_path)
+ pysidedeploy_config.recipe_dir = ((pysidedeploy_config.generated_files_path
+ / "recipes").resolve())
self.set_value('app', "p4a.local_recipes", str(pysidedeploy_config.recipe_dir))
# add permissions
# change final apk/aab path
self.set_value("buildozer", "bin_dir", str(pysidedeploy_config.exe_dir.resolve()))
+ # set application icon
+ self.set_value("app", "icon.filename", pysidedeploy_config.icon)
+
self.update_config()
def __get_dependency_files(self, modules: List[str], arch: str) -> List[zipfile.Path]:
else:
continue
- absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins" /
- plugin_module_folder)
+ absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins"
+ / plugin_module_folder)
if not absolute_plugin_module_folder.is_dir():
logging.warning(f"[DEPLOY] Qt plugin folder '{plugin_module_folder}' does not"
dry_run = False
@staticmethod
- def initialize(pysidedeploy_config: Config, generated_files_path: Path):
+ def initialize(pysidedeploy_config: Config):
project_dir = Path(pysidedeploy_config.project_dir)
buildozer_spec = project_dir / "buildozer.spec"
if buildozer_spec.exists():
return
# creates buildozer.spec config file
- command = ["buildozer", "init"]
+ command = [sys.executable, "-m", "buildozer", "init"]
run_command(command=command, dry_run=Buildozer.dry_run)
if not Buildozer.dry_run:
if not buildozer_spec.exists():
raise RuntimeError(f"buildozer.spec not found in {Path.cwd()}")
- BuildozerConfig(buildozer_spec, pysidedeploy_config, generated_files_path)
+ BuildozerConfig(buildozer_spec, pysidedeploy_config)
@staticmethod
def create_executable(mode: str):
- command = ["buildozer", "android", mode]
+ command = [sys.executable, "-m", "buildozer", "android", mode]
run_command(command=command, dry_run=Buildozer.dry_run)
shutil.copyfile(lib_dir.parent.parent / "libpyside6.abi3.so",
Path(self.ctx.get_libs_dir(arch.arch)) / "libpyside6.abi3.so")
- {% for module in qt_modules %}
+ {% for module in qt_modules %} # noqa: E999
shutil.copyfile(lib_dir.parent.parent / f"Qt{{ module }}.abi3.so",
Path(self.ctx.get_libs_dir(arch.arch)) / "Qt{{ module }}.abi3.so")
- {% if module == "Qml" -%}
+ {% if module == "Qml" -%} # noqa: E999
shutil.copyfile(lib_dir.parent.parent / "libpyside6qml.abi3.so",
Path(self.ctx.get_libs_dir(arch.arch)) / "libpyside6qml.abi3.so")
- {% endif %}
- {% endfor %}
+ {% endif %} # noqa: E999
+ {% endfor %} # noqa: E999
- {% for lib in qt_local_libs %}
+ {% for lib in qt_local_libs %} # noqa: E999
lib_path = lib_dir / f"lib{{ lib }}_{arch.arch}.so"
if lib_path.exists():
shutil.copyfile(lib_path,
Path(self.ctx.get_libs_dir(arch.arch)) / f"lib{{ lib }}_{arch.arch}.so")
- {% endfor %}
+ {% endfor %} # noqa: E999
- {% for plugin_category,plugin_name in qt_plugins %}
+ {% for plugin_category,plugin_name in qt_plugins %} # noqa: E999
plugin_path = (lib_dir.parent / "plugins" / "{{ plugin_category }}" /
f"libplugins_{{ plugin_category }}_{{ plugin_name }}_{arch.arch}.so")
if plugin_path.exists():
shutil.copyfile(plugin_path,
(Path(self.ctx.get_libs_dir(arch.arch)) /
f"libplugins_{{ plugin_category }}_{{ plugin_name }}_{arch.arch}.so"))
- {% endfor %}
+ {% endfor %} # noqa: E999
recipe = PySideRecipe()
from project import ProjectData
from .commands import run_qmlimportscanner
+from . import DEFAULT_APP_ICON
# Some QML plugins like QtCore are excluded from this list as they don't contribute much to
# executable size. Excluding them saves the extra processing of checking for them in files
EXCLUDED_QML_PLUGINS = {"QtQuick", "QtQuick3D", "QtCharts", "QtWebEngine", "QtTest", "QtSensors"}
-# TODO: Move this to android module. Fix circular import.
-ANDROID_NDK_VERSION = "25c"
-ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
class BaseConfig:
"""
def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
- android_data, is_android: bool, existing_config_file: bool = False):
+ existing_config_file: bool = False):
super().__init__(config_file=config_file, existing_config_file=existing_config_file)
self._dry_run = dry_run
+ self.qml_modules = set()
# set source_file
self.source_file = Path(
self.set_or_fetch(config_property_val=source_file, config_property_key="input_file")
- )
+ ).resolve()
# set python path
self.python_path = Path(
self.title = self.get_value("app", "title")
+ # set application icon
+ config_icon = self.get_value("app", "icon")
+ if config_icon:
+ self.icon = str(Path(config_icon).resolve())
+ else:
+ self.icon = DEFAULT_APP_ICON
+
self.project_dir = None
if self.get_value("app", "project_dir"):
self.project_dir = Path(self.get_value("app", "project_dir")).absolute()
else:
self._find_and_set_excluded_qml_plugins()
- # Android
- if is_android:
- if android_data.wheel_pyside:
- self.wheel_pyside = android_data.wheel_pyside
- else:
- wheel_pyside_temp = self.get_value("qt", "wheel_pyside")
- if not wheel_pyside_temp:
- raise RuntimeError("[DEPLOY] Unable to find PySide6 Android wheel")
- self.wheel_pyside = Path(wheel_pyside_temp).resolve()
-
- if android_data.wheel_shiboken:
- self.wheel_shiboken = android_data.wheel_shiboken
- else:
- wheel_shiboken_temp = self.get_value("qt", "wheel_shiboken")
- if not wheel_shiboken_temp:
- raise RuntimeError("[DEPLOY] Unable to find shiboken6 Android wheel")
- self.wheel_shiboken = Path(wheel_shiboken_temp).resolve()
-
- self.ndk_path = None
- if android_data.ndk_path:
- # from cli
- self.ndk_path = android_data.ndk_path
- else:
- # from config
- ndk_path_temp = self.get_value("buildozer", "ndk_path")
- if ndk_path_temp:
- self.ndk_path = Path(ndk_path_temp)
- else:
- ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
- / f"android-ndk-r{ANDROID_NDK_VERSION}")
- if ndk_path_temp.exists():
- self.ndk_path = ndk_path_temp
-
- if self.ndk_path:
- print(f"Using Android NDK: {str(self.ndk_path)}")
- else:
- raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
- "path either from the CLI or from pysidedeploy.spec")
-
- self.sdk_path = None
- if android_data.sdk_path:
- self.sdk_path = android_data.sdk_path
- else:
- sdk_path_temp = self.get_value("buildozer", "sdk_path")
- if sdk_path_temp:
- self.sdk_path = Path(sdk_path_temp)
- else:
- sdk_path_temp = ANDROID_DEPLOY_CACHE / "android-sdk"
- if sdk_path_temp.exists():
- self.sdk_path = sdk_path_temp
- else:
- logging.info("[DEPLOY] Use default SDK from buildozer")
-
- if self.sdk_path:
- print(f"Using Android SDK: {str(self.sdk_path)}")
-
- recipe_dir_temp = self.get_value("buildozer", "recipe_dir")
- self.recipe_dir = Path(recipe_dir_temp) if recipe_dir_temp else None
-
- jars_dir_temp = self.get_value("buildozer", "jars_dir")
- self.jars_dir = Path(jars_dir_temp) if jars_dir_temp else None
-
- self._modules = []
- if self.get_value("buildozer", "modules"):
- self.modules = self.get_value("buildozer", "modules").split(",")
-
- self.arch = self.get_value("buildozer", "arch")
-
- self._local_libs = []
- if self.get_value("buildozer", "local_libs"):
- self.local_libs = self.get_value("buildozer", "local_libs").split(",")
-
- self._qt_plugins = []
- if self.get_value("qt", "plugins"):
- self._qt_plugins = self.get_value("qt", "plugins").split(",")
-
- self._mode = self.get_value("buildozer", "mode")
+ self._generated_files_path = self.project_dir / "deployment"
def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"):
"""
def dry_run(self):
return self._dry_run
+ @property
+ def generated_files_path(self):
+ return self._generated_files_path
+
@property
def qml_files(self):
return self._qml_files
self._title = title
self.set_value("app", "title", title)
+ @property
+ def icon(self):
+ return self._icon
+
+ @icon.setter
+ def icon(self, icon):
+ self._icon = icon
+ self.set_value("app", "icon", icon)
+
@property
def source_file(self):
return self._source_file
def excluded_qml_plugins(self, excluded_qml_plugins):
self._excluded_qml_plugins = excluded_qml_plugins
- @property
- def recipe_dir(self):
- return self._recipe_dir
-
- @recipe_dir.setter
- def recipe_dir(self, recipe_dir: Path):
- self._recipe_dir = recipe_dir.resolve() if recipe_dir else None
- if self._recipe_dir:
- self.set_value("buildozer", "recipe_dir", str(self._recipe_dir))
-
- def recipes_exist(self):
- if not self._recipe_dir:
- return False
-
- pyside_recipe_dir = Path(self.recipe_dir) / "PySide6"
- shiboken_recipe_dir = Path(self.recipe_dir) / "shiboken6"
-
- return pyside_recipe_dir.is_dir() and shiboken_recipe_dir.is_dir()
-
- @property
- def jars_dir(self) -> Path:
- return self._jars_dir
-
- @jars_dir.setter
- def jars_dir(self, jars_dir: Path):
- self._jars_dir = jars_dir.resolve() if jars_dir else None
- if self._jars_dir:
- self.set_value("buildozer", "jars_dir", str(self._jars_dir))
-
- @property
- def modules(self):
- return self._modules
-
- @modules.setter
- def modules(self, modules):
- self._modules = modules
- self.set_value("buildozer", "modules", ",".join(modules))
-
- @property
- def local_libs(self):
- return self._local_libs
-
- @local_libs.setter
- def local_libs(self, local_libs):
- self._local_libs = local_libs
- self.set_value("buildozer", "local_libs", ",".join(local_libs))
-
- @property
- def qt_plugins(self):
- return self._qt_plugins
-
- @qt_plugins.setter
- def qt_plugins(self, qt_plugins):
- self._qt_plugins = qt_plugins
- self.set_value("qt", "plugins", ",".join(qt_plugins))
-
- @property
- def ndk_path(self):
- return self._ndk_path
-
- @ndk_path.setter
- def ndk_path(self, ndk_path: Path):
- self._ndk_path = ndk_path.resolve() if ndk_path else None
- if self._ndk_path:
- self.set_value("buildozer", "ndk_path", str(self._ndk_path))
-
- @property
- def sdk_path(self) -> Path:
- return self._sdk_path
-
- @sdk_path.setter
- def sdk_path(self, sdk_path: Path):
- self._sdk_path = sdk_path.resolve() if sdk_path else None
- if self._sdk_path:
- self.set_value("buildozer", "sdk_path", str(self._sdk_path))
-
- @property
- def arch(self):
- return self._arch
-
- @arch.setter
- def arch(self, arch):
- self._arch = arch
- self.set_value("buildozer", "arch", arch)
-
- @property
- def mode(self):
- return self._mode
-
@property
def exe_dir(self):
return self._exe_dir
def exe_dir(self, exe_dir: Path):
self._exe_dir = exe_dir
- @property
- def wheel_pyside(self) -> Path:
- return self._wheel_pyside
-
- @wheel_pyside.setter
- def wheel_pyside(self, wheel_pyside: Path):
- self._wheel_pyside = wheel_pyside.resolve() if wheel_pyside else None
- if self._wheel_pyside:
- self.set_value("qt", "wheel_pyside", str(self._wheel_pyside))
-
- @property
- def wheel_shiboken(self) -> Path:
- return self._wheel_shiboken
-
- @wheel_shiboken.setter
- def wheel_shiboken(self, wheel_shiboken: Path):
- self._wheel_shiboken = wheel_shiboken.resolve() if wheel_shiboken else None
- if self._wheel_shiboken:
- self.set_value("qt", "wheel_shiboken", str(self._wheel_shiboken))
-
def _find_and_set_qml_files(self):
"""Fetches all the qml_files in the folder and sets them if the
field qml_files is empty in the config_dir"""
def _find_and_set_excluded_qml_plugins(self):
if self.qml_files:
- included_qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
- dry_run=self.dry_run))
- self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(included_qml_modules)
+ self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
+ dry_run=self.dry_run))
+ self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
# needed for dry_run testing
self.excluded_qml_plugins = sorted(self.excluded_qml_plugins)
# Path to .pyproject project file
project_file =
+# Application icon
+icon =
+
[python]
# Python path
# python packages to install
# ordered-set: increase compile time performance of nuitka packaging
# zstandard: provides final executable size optimization
-packages = nuitka==1.5.4,ordered_set,zstandard
+packages = nuitka==1.8.0,ordered_set,zstandard
# buildozer: for deploying Android application
android_packages = buildozer==1.5.0,cython==0.29.33
[qt]
# Comma separated path to QML files required
-# normally all the QML files are added automatically
+# normally all the QML files required by the project are added automatically
qml_files =
# excluded qml plugin binaries
excluded_qml_plugins =
+[android]
+
# path to PySide wheel
wheel_pyside =
# (str) specify any extra nuitka arguments
# eg: extra_args = --show-modules --follow-stdlib
-extra_args = --quiet --noinclude-qt-translations=True
+extra_args = --quiet --noinclude-qt-translations
[buildozer]
return False
-def cleanup(generated_files_path: Path, config: Config, is_android: bool = False):
+def cleanup(config: Config, is_android: bool = False):
"""
Cleanup the generated build folders/files
"""
- if generated_files_path.exists():
- shutil.rmtree(generated_files_path)
+ if config.generated_files_path.exists():
+ shutil.rmtree(config.generated_files_path)
logging.info("[DEPLOY] Deployment directory purged")
if is_android:
logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
-def get_config(python_exe: Path, dry_run: bool = False, config_file: Path = None, main_file:
- Path = None, android_data=None, is_android: bool = False):
+def create_config_file(dry_run: bool = False, config_file: Path = None, main_file: Path = None):
"""
Sets up a new pysidedeploy.spec or use an existing config file
"""
- config_file_exists = config_file and Path(config_file).exists()
-
- if main_file and not config_file_exists:
+ if main_file:
if main_file.parent != Path.cwd():
config_file = main_file.parent / "pysidedeploy.spec"
else:
config_file = Path.cwd() / "pysidedeploy.spec"
- if config_file_exists:
- logging.info(f"[DEPLOY] Using existing config file {config_file}")
- else:
- logging.info(f"[DEPLOY] Creating config file {config_file}")
- if not dry_run:
- shutil.copy(Path(__file__).parent / "default.spec", config_file)
+ logging.info(f"[DEPLOY] Creating config file {config_file}")
+ if not dry_run:
+ shutil.copy(Path(__file__).parent / "default.spec", config_file)
# the config parser needs a reference to parse. So, in the case of --dry-run
# use the default.spec file.
- if dry_run and not config_file_exists:
+ if dry_run:
config_file = Path(__file__).parent / "default.spec"
- config = Config(config_file=config_file, source_file=main_file, python_exe=python_exe,
- dry_run=dry_run, android_data=android_data, is_android=is_android,
- existing_config_file=config_file_exists)
-
- return config
+ return config_file
def setup_python(dry_run: bool, force: bool, init: bool):
"""
Installs the python package dependencies for the target deployment platform
"""
+ packages = config.get_value("python", packages).split(",")
if not init:
# install packages needed for deployment
logging.info("[DEPLOY] Installing dependencies")
- packages = config.get_value("python", packages).split(",")
python.install(packages=packages)
# nuitka requires patchelf to make patchelf rpath changes for some Qt files
if sys.platform.startswith("linux") and not is_android:
python.install(packages=["patchelf"])
+ elif is_android:
+ # install only buildozer
+ logging.info("[DEPLOY] Installing buildozer")
+ buildozer_package_with_version = ([package for package in packages
+ if package.startswith("buildozer")])
+ python.install(packages=list(buildozer_package_with_version))
-def finalize(generated_files_path: Path, config: Config):
+def finalize(config: Config):
"""
Copy the executable into the final location
For Android deployment, this is done through buildozer
"""
- generated_exec_path = generated_files_path / (config.source_file.stem + EXE_FORMAT)
+ generated_exec_path = config.generated_files_path / (config.source_file.stem + EXE_FORMAT)
if generated_exec_path.exists() and config.exe_dir:
shutil.copy(generated_exec_path, config.exe_dir)
print("[DEPLOY] Executed file created in "
def __init__(self, nuitka):
self.nuitka = nuitka
+ @staticmethod
+ def icon_option():
+ if sys.platform == "linux":
+ return "--linux-icon"
+ elif sys.platform == "win32":
+ return "--windows-icon-from-ico"
+ else:
+ return "--macos-app-icon"
+
def create_executable(self, source_file: Path, extra_args: str, qml_files: List[Path],
- excluded_qml_plugins, dry_run: bool):
+ excluded_qml_plugins: List[str], icon: str, dry_run: bool):
extra_args = extra_args.split()
qml_args = []
if qml_files:
f"--output-dir={output_dir}",
]
command.extend(extra_args + qml_args)
-
- if sys.platform == "linux":
- linux_icon = str(Path(__file__).parent / "pyside_icon.jpg")
- command.append(f"--linux-onefile-icon={linux_icon}")
+ command.append(f"{self.__class__.icon_option()}={icon}")
command_str, _ = run_command(command=command, dry_run=dry_run)
return command_str
from importlib.metadata import version
from pathlib import Path
-from . import Config, Nuitka, run_command
+from . import Nuitka, run_command
IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
". Use 'from PySide6 import <module>' or pass the module"
return False
def install(self, packages: list = None):
- _, installed_packages = run_command(command=[str(self.exe), "-m", "pip", "freeze"], dry_run=False
- , fetch_output=True)
+ _, installed_packages = run_command(command=[str(self.exe), "-m", "pip", "freeze"],
+ dry_run=False, fetch_output=True)
installed_packages = [p.decode().split('==')[0] for p in installed_packages.split()]
for package in packages:
package_info = package.split('==')
def is_installed(self, package):
return bool(util.find_spec(package))
- def create_executable(self, source_file: Path, extra_args: str, config: Config):
+ def create_executable(self, source_file: Path, extra_args: str, config):
if config.qml_files:
logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
- command_str = self.nuitka.create_executable(
- source_file=source_file,
- extra_args=extra_args,
- qml_files=config.qml_files,
- excluded_qml_plugins=config.excluded_qml_plugins,
- dry_run=self.dry_run,
- )
+ command_str = self.nuitka.create_executable(source_file=source_file,
+ extra_args=extra_args,
+ qml_files=config.qml_files,
+ excluded_qml_plugins=(config.
+ excluded_qml_plugins),
+ icon=config.icon,
+ dry_run=self.dry_run)
return command_str
"new-ui": ProjectType.WIDGET_FORM,
"new-widget": ProjectType.WIDGET}
+
class Project:
"""
Class to wrap the various operations on Project
qmltypes_file = self._qml_module_dir / f"{stem}.qmltypes"
cpp_file = self._qml_module_dir / f"{stem}_qmltyperegistrations.cpp"
cmd = [QMLTYPEREGISTRAR_CMD, "--generate-qmltypes",
- os.fspath(qmltypes_file), "-o", os.fspath(cpp_file),
+ os.fspath(qmltypes_file), "-o", os.fspath(cpp_file),
os.fspath(file)]
cmd.extend(self._qml_project_data.registrar_options())
return ([qmltypes_file, cpp_file], cmd)
def new_project(directory_s: str,
- project_type: ProjectType=ProjectType.WIDGET_FORM) -> int:
+ project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
directory = Path(directory_s)
if directory.exists():
print(f"{directory_s} already exists.", file=sys.stderr)
MOD_CMD, QML_IMPORT_MAJOR_VERSION, QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME,
QT_MODULES)
+
def is_python_file(file: Path) -> bool:
return (file.suffix == ".py"
or sys.platform == "win32" and file.suffix == ".pyw")
# __main__ not found
print(
- "Python file with main function not found. Add the file to" f" {project_file}",
+ "Python file with main function not found. Add the file to" f" {self.project_file}",
file=sys.stderr,
)
sys.exit(1)
if v:
qml_project_data.qt_modules = v
return (has_class, qml_project_data)
-
import sys
import sysconfig
from pathlib import Path
-from subprocess import PIPE, Popen
import PySide6 as ref_mod
VIRTUAL_ENV = "VIRTUAL_ENV"
+def is_pyenv_python():
+ pyenv_root = os.environ.get("PYENV_ROOT")
+
+ if pyenv_root:
+ resolved_exe = Path(sys.executable).resolve()
+ if str(resolved_exe).startswith(pyenv_root):
+ return True
+
+ return False
+
+
def is_virtual_env():
return sys.prefix != sys.base_prefix
exe = pyside_dir / qt_tool
cmd = [os.fspath(exe)] + args
- proc = Popen(cmd, stderr=PIPE)
- out, err = proc.communicate()
- if err:
- msg = err.decode("utf-8")
+ returncode = subprocess.call(cmd)
+ if returncode != 0:
command = ' '.join(cmd)
- print(f"Error: {msg}\nwhile executing '{command}'")
- sys.exit(proc.returncode)
+ print(f"'{command}' returned {returncode}", file=sys.stderr)
+ sys.exit(returncode)
def pyside_script_wrapper(script_name):
# Determine library name (examples/utils/pyside_config.py)
version = f'{major_version}.{minor_version}'
library_name = f'libpython{version}{sys.abiflags}.so'
+ if is_pyenv_python():
+ library_name = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
os.environ['LD_PRELOAD'] = library_name
elif sys.platform == 'darwin':
library_name = sysconfig.get_config_var("LDLIBRARY")
framework_prefix = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")
- lib_path = os.fspath(Path(framework_prefix) / library_name)
+ lib_path = None
+ if framework_prefix:
+ lib_path = os.fspath(Path(framework_prefix) / library_name)
+ elif is_pyenv_python():
+ lib_path = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
+ else:
+ # ideally this should never be reached because the system Python and Python installed
+ # from python.org are all framework builds
+ print("Unable to find Python library directory. Use a framework build of Python.",
+ file=sys.stderr)
+ sys.exit(0)
os.environ['DYLD_INSERT_LIBRARIES'] = lib_path
elif sys.platform == 'win32':
# Find Python DLLs from the base installation
if not sys.platform == "linux":
print("pyside6-android-deploy only works from a Linux host")
else:
- dependent_packages = ["jinja2", "pkginfo"]
- for dependent_package in dependent_packages:
- if not bool(importlib.util.find_spec(dependent_package)):
- command = [sys.executable, "-m", "pip", "install", dependent_package]
- subprocess.run(command)
+ android_requirements_file = Path(__file__).parent / "requirements-android.txt"
+ with open(android_requirements_file, 'r', encoding='UTF-8') as file:
+ while line := file.readline():
+ dependent_package = line.rstrip()
+ if not bool(importlib.util.find_spec(dependent_package)):
+ command = [sys.executable, "-m", "pip", "install", dependent_package]
+ subprocess.run(command)
pyside_script_wrapper("android_deploy.py")
if apptype == "core":
component = QQmlComponent(engine, qml_file)
obj = component.create()
- filtered_attributes = {k: v for k, v in vars(obj).items() if type(v) != SignalInstance}
+ filtered_attributes = {k: v for k, v in vars(obj).items() if type(v) is not SignalInstance}
logging.info("qml: component object attributes are")
pprint(filtered_attributes)
del engine
sys.exit(-1)
qquick_view = False
- if type(rootObjects[0]) != QQuickWindow and qquick_present:
+ if isinstance(rootObjects[0], QQuickWindow) and qquick_present:
logging.info("qml: loading with QQuickView")
viewer = QQuickView()
viewer.setSource(qml_file)
import sys
-from PySide6.QtCore import qVersion, Qt
+from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPainter, QPaintEvent, QShortcut
from PySide6.QtWidgets import QApplication, QWidget
with tempfile.NamedTemporaryFile(suffix=".cpp") as tf:
tf.write(actual_code.encode('utf-8'))
tf.flush()
- diff_cmd = ["diff", "-u", expected_file, tf.name]
+ diff_cmd = ["diff", "-u", expected_file, tf.name]
subprocess.run(diff_cmd)
def test_examples():
dir = Path(__file__).resolve().parent
tool = dir.parents[1] / "qtpy2cpp.py"
- assert(tool.is_file)
+ assert tool.is_file
for test_file in (dir / "baseline").glob("*.py"):
- assert(test_file.is_file)
+ assert test_file.is_file
expected_file = test_file.parent / (test_file.stem + ".cpp")
if expected_file.is_file():
actual_code = run_converter(tool, test_file)
- assert(actual_code)
+ assert actual_code
expected_code = expected_file.read_text()
# Strip the license
code_start = expected_code.find("// Converted from")
- assert(code_start != -1)
+ assert code_start != -1
expected_code = expected_code[code_start:]
if actual_code != expected_code:
diff_code(actual_code, expected_file)
- assert(actual_code == expected_code)
+ assert actual_code == expected_code
else:
print(f"Warning, {test_file} is missing a .cpp file.",
file=sys.stderr)
from .formatter import (CppFormatter, format_for_loop, format_literal,
format_name_constant,
- format_reference, format_start_function_call,
- write_import, write_import_from)
+ format_reference, write_import, write_import_from)
from .nodedump import debug_format_node
from .qt import ClassFlag, qt_class_flags
--- /dev/null
+jinja2
+pkginfo
set(pyside_MAJOR_VERSION "6")
set(pyside_MINOR_VERSION "6")
-set(pyside_MICRO_VERSION "1")
+set(pyside_MICRO_VERSION "2")
set(pyside_PRE_RELEASE_VERSION_TYPE "")
set(pyside_PRE_RELEASE_VERSION "")
get_rpath_base_token(base)
if (${STANDALONE})
- set(CMAKE_INSTALL_RPATH ${base}/ ${base}/Qt/lib)
+ set(CMAKE_INSTALL_RPATH ${base}/ ${base}/Qt/lib ${base}/../shiboken6/)
else()
- set(CMAKE_INSTALL_RPATH ${base}/ ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS})
+ set(CMAKE_INSTALL_RPATH ${base}/ ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS} ${base}/../shiboken6/)
endif()
add_subdirectory(libpyside)
from .futures import QAsyncioFuture
from .tasks import QAsyncioTask
+import asyncio
+import typing
+
__all__ = [
"QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
"QAsyncioHandle", "QAsyncioTimerHandle",
"QAsyncioFuture", "QAsyncioTask"
]
+
+
+def run(coro: typing.Optional[typing.Coroutine] = None,
+ keep_running: bool = True,
+ quit_qapp: bool = True, *,
+ debug: typing.Optional[bool] = None) -> None:
+ """Run the QtAsyncio event loop."""
+
+ # Event loop policies are expected to be deprecated with Python 3.13, with
+ # subsequent removal in Python 3.15. At that point, part of the current
+ # logic of the QAsyncioEventLoopPolicy constructor will have to be moved
+ # here and/or to a loop factory class (to be provided as an argument to
+ # asyncio.run()), namely setting up the QCoreApplication and the SIGINT
+ # handler.
+ #
+ # More details:
+ # https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553 # noqa: E501
+ asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy(quit_qapp=quit_qapp))
+
+ if keep_running:
+ if coro:
+ asyncio.ensure_future(coro)
+ asyncio.get_event_loop().run_forever()
+ else:
+ if coro:
+ asyncio.run(coro, debug=debug)
+ else:
+ raise RuntimeError(
+ "QtAsyncio was set to keep running after the coroutine "
+ "finished, but no coroutine was provided.")
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-from PySide6.QtCore import QCoreApplication, QDateTime, QEventLoop, QObject, QTimer, QThread, Slot
+from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
+ QEventLoop, QObject, QTimer, QThread, Slot)
from . import futures
from . import tasks
import contextvars
import enum
import os
+import signal
import socket
import subprocess
import typing
+import warnings
__all__ = [
"QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
- def __init__(self, application: typing.Optional[QCoreApplication] = None) -> None:
+ def __init__(self,
+ application: typing.Optional[QCoreApplication] = None,
+ quit_qapp: bool = True) -> None:
super().__init__()
if application is None:
if QCoreApplication.instance() is None:
else:
application = QCoreApplication.instance()
self._application: QCoreApplication = application # type: ignore[assignment]
+ self._quit_qapp = quit_qapp
self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
def get_event_loop(self) -> asyncio.AbstractEventLoop:
if self._event_loop is None:
self._event_loop = QAsyncioEventLoop(self._application)
self._event_loop = loop
def new_event_loop(self) -> asyncio.AbstractEventLoop:
- return QAsyncioEventLoop(self._application)
+ return QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
+
+ def get_child_watcher(self) -> asyncio.AbstractChildWatcher:
+ raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
+
+ def set_child_watcher(self, watcher: asyncio.AbstractChildWatcher) -> None:
+ raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
if not self._loop.is_closed():
self._loop.call_soon_threadsafe(self._future.set_exception, e)
- def __init__(self, application: QCoreApplication) -> None:
+ def __init__(self,
+ application: QCoreApplication, quit_qapp: bool = True) -> None:
asyncio.BaseEventLoop.__init__(self)
QObject.__init__(self)
self._application: QCoreApplication = application
+ self._quit_qapp = quit_qapp
self._thread = QThread.currentThread()
self._closed = False
return
future.get_loop().stop()
- def run_until_complete(self, future: futures.QAsyncioFuture) -> typing.Any: # type: ignore[override]
+ def run_until_complete(self,
+ future: futures.QAsyncioFuture) -> typing.Any: # type: ignore[override]
if self.is_closed():
raise RuntimeError("Event loop is closed")
if self.is_running():
else:
return
self._quit_from_inside = True
- self._application.quit()
+ if self._quit_qapp:
+ self._application.quit()
def is_running(self) -> bool:
return self._thread.loopLevel() > 0
return
if self._default_executor is not None:
self._default_executor.shutdown(wait=False)
- self._application.shutdown()
+ if self._quit_qapp:
+ self._application.shutdown()
self._closed = True
async def shutdown_asyncgens(self) -> None:
self._asyncgens.clear()
- async def shutdown_default_executor(self) -> None:
+ async def shutdown_default_executor(self, # type: ignore[override]
+ timeout: typing.Union[int, float, None] = None) -> None:
+ shutdown_successful = False
+ if timeout is not None:
+ deadline_timer = QDeadlineTimer(int(timeout * 1000))
+ else:
+ deadline_timer = QDeadlineTimer(QDeadlineTimer.Forever)
+
if self._default_executor is None:
return
future = self.create_future()
try:
await future
finally:
- thread.wait()
+ shutdown_successful = thread.wait(deadline_timer)
+
+ if timeout is not None and not shutdown_successful:
+ warnings.warn(
+ f"Could not shutdown the default executor within {timeout} seconds",
+ RuntimeWarning, stacklevel=2)
+ self._default_executor.shutdown(wait=False)
# Scheduling callbacks
+ def _call_soon_impl(self, callback: typing.Callable, *args: typing.Any,
+ context: typing.Optional[contextvars.Context] = None,
+ is_threadsafe: typing.Optional[bool] = False) -> asyncio.Handle:
+ return self._call_later_impl(0, callback, *args, context=context,
+ is_threadsafe=is_threadsafe)
+
def call_soon(self, callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None):
- return self.call_later(0, callback, *args, context=context)
+ context: typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+ return self._call_soon_impl(callback, *args, context=context, is_threadsafe=False)
- def call_soon_threadsafe(self, callback: typing.Callable, # type: ignore[override]
- *args: typing.Any,
+ def call_soon_threadsafe(self, callback: typing.Callable, *args: typing.Any,
context:
- typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
- if self.is_closed():
- raise RuntimeError("Event loop is closed")
+ typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
if context is None:
context = contextvars.copy_context()
- return self.call_soon(callback, *args, context=context)
+ return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
- def call_later(self, delay: typing.Union[int, float], # type: ignore[override]
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
+ def _call_later_impl(self, delay: typing.Union[int, float],
+ callback: typing.Callable, *args: typing.Any,
+ context: typing.Optional[contextvars.Context] = None,
+ is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
if not isinstance(delay, (int, float)):
raise TypeError("delay must be an int or float")
- return self.call_at(self.time() + delay, callback, *args, context=context)
+ return self._call_at_impl(self.time() + delay, callback, *args, context=context,
+ is_threadsafe=is_threadsafe)
- def call_at(self, when: typing.Union[int, float], # type: ignore[override]
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
+ def call_later(self, delay: typing.Union[int, float],
+ callback: typing.Callable, *args: typing.Any,
+ context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+ return self._call_later_impl(delay, callback, *args, context=context, is_threadsafe=False)
+
+ def _call_at_impl(self, when: typing.Union[int, float],
+ callback: typing.Callable, *args: typing.Any,
+ context: typing.Optional[contextvars.Context] = None,
+ is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
if not isinstance(when, (int, float)):
raise TypeError("when must be an int or float")
if self.is_closed():
raise RuntimeError("Event loop is closed")
- return QAsyncioTimerHandle(when, callback, args, self, context)
+ return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
+
+ def call_at(self, when: typing.Union[int, float],
+ callback: typing.Callable, *args: typing.Any,
+ context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+ return self._call_at_impl(when, callback, *args, context=context, is_threadsafe=False)
def time(self) -> float:
return QDateTime.currentMSecsSinceEpoch() / 1000
def default_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
# TODO
- print(context["message"])
+ if context["message"]:
+ print(context["message"])
def call_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
if self._exception_handler is not None:
DONE = enum.auto()
def __init__(self, callback: typing.Callable, args: typing.Tuple,
- loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context]) -> None:
+ loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
+ is_threadsafe: typing.Optional[bool] = False) -> None:
self._callback = callback
self._args = args
self._loop = loop
self._context = context
+ self._is_threadsafe = is_threadsafe
self._timeout = 0
def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
if not self._loop.is_closed() and not self._loop._quit_from_outside:
- QTimer.singleShot(timeout, self._loop, func)
+ if self._is_threadsafe:
+ QTimer.singleShot(timeout, self._loop, func)
+ else:
+ QTimer.singleShot(timeout, func)
def _start(self) -> None:
self._schedule_event(self._timeout, lambda: self._cb())
return self._state == QAsyncioHandle.HandleState.CANCELLED
-class QAsyncioTimerHandle(QAsyncioHandle):
+class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
def __init__(self, when: float, callback: typing.Callable, args: typing.Tuple,
- loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context]) -> None:
- super().__init__(callback, args, loop, context)
+ loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
+ is_threadsafe: typing.Optional[bool] = False) -> None:
+ QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
self._when = when
self._timeout = int(max(self._when - self._loop.time(), 0) * 1000)
- super()._start()
+ QAsyncioHandle._start(self)
# Override this so that timer.start() is only called once at the end
# of the constructor for both QtHandle and QtTimerHandle.
def __init__(self, *, loop: typing.Optional["events.QAsyncioEventLoop"] = None,
context: typing.Optional[contextvars.Context] = None) -> None:
+ self._loop: "events.QAsyncioEventLoop"
if loop is None:
- self._loop = asyncio.events.get_event_loop()
+ self._loop = asyncio.events.get_event_loop() # type: ignore[assignment]
else:
self._loop = loop
self._context = context
self._state = QAsyncioFuture.FutureState.PENDING
self._result: typing.Any = None
- self._exception: typing.Optional[Exception] = None
+ self._exception: typing.Optional[BaseException] = None
self._callbacks: typing.List[typing.Callable] = list()
self._asyncio_future_blocking = True
yield self
if not self.done():
- raise RuntimeError("await was not used with a Future")
+ raise RuntimeError("await was not used with a Future or Future-like object")
return self.result()
__iter__ = __await__
def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
- if self._loop.is_running():
- for cb in self._callbacks:
- self._loop.call_soon(
- cb, self, context=context if context else self._context)
+ for cb in self._callbacks:
+ self._loop.call_soon(
+ cb, self, context=context if context else self._context)
def result(self) -> typing.Union[typing.Any, Exception]:
if self._state == QAsyncioFuture.FutureState.DONE_WITH_RESULT:
self._callbacks = [_cb for _cb in self._callbacks if _cb != cb]
return original_len - len(self._callbacks)
- def cancel(self) -> bool:
+ def cancel(self, msg: typing.Optional[str] = None) -> bool:
if self.done():
return False
self._state = QAsyncioFuture.FutureState.CANCELLED
+ self._cancel_message = msg
self._schedule_callbacks()
return True
- def exception(self) -> typing.Optional[Exception]:
+ def exception(self) -> typing.Optional[BaseException]:
if self._state == QAsyncioFuture.FutureState.CANCELLED:
raise asyncio.CancelledError
if self.done():
self._cancellation_requests = 0
+ self._future_to_await: typing.Optional[asyncio.Future] = None
+ self._cancel_message: typing.Optional[str] = None
+
asyncio._register_task(self) # type: ignore[arg-type]
def __repr__(self) -> str:
class QtTaskApiMisuseError(Exception):
pass
- def __await__(self) -> None: # type: ignore[override]
- # This function is not inherited from the Future APIs.
- raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot be awaited")
-
- def __iter__(self) -> None: # type: ignore[override]
- # This function is not inherited from the Future APIs.
- raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot be iterated over")
-
def set_result(self, result: typing.Any) -> None: # type: ignore[override]
# This function is not inherited from the Future APIs.
raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set results")
if self.done():
return
result = None
+ self._future_to_await = None
try:
asyncio._enter_task(self._loop, self) # type: ignore[arg-type]
except StopIteration as e:
self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_RESULT
self._result = e.value
- except concurrent.futures.CancelledError as e:
+ except (concurrent.futures.CancelledError, asyncio.exceptions.CancelledError) as e:
self._state = futures.QAsyncioFuture.FutureState.CANCELLED
self._exception = e
except BaseException as e:
self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION
- self._exception = e # type: ignore[assignment]
+ self._exception = e
else:
if asyncio.futures.isfuture(result):
result.add_done_callback(
self._step, context=self._context) # type: ignore[arg-type]
+ self._future_to_await = result
elif result is None:
self._loop.call_soon(self._step, context=self._context)
else:
return False
self._cancel_message = msg
self._handle.cancel()
- self._state = futures.QAsyncioFuture.FutureState.CANCELLED
+ if self._future_to_await is not None:
+ self._future_to_await.cancel(msg)
return True
def uncancel(self) -> None:
${QtCharts_GEN_DIR}/qcategoryaxis_wrapper.cpp
${QtCharts_GEN_DIR}/qchart_wrapper.cpp
${QtCharts_GEN_DIR}/qchartview_wrapper.cpp
+${QtCharts_GEN_DIR}/qcoloraxis_wrapper.cpp
${QtCharts_GEN_DIR}/qdatetimeaxis_wrapper.cpp
${QtCharts_GEN_DIR}/qhbarmodelmapper_wrapper.cpp
${QtCharts_GEN_DIR}/qhboxplotmodelmapper_wrapper.cpp
</modify-argument>
</modify-function>
</object-type>
+ <object-type name="QColorAxis"/>
<object-type name="QDateTimeAxis"/>
<object-type name="QHBarModelMapper"/>
<object-type name="QHBoxPlotModelMapper"/>
<rejection class="*" field-name="d_ptr"/>
<rejection class="QMetaMethod" enum-name="Attributes"/>
<rejection class="QMetaMethod" field-name="data"/>
+ <!-- Note: Default parameter values of Disambiguated_t as defined by
+ QT6_DECL_NEW_OVERLOAD_TAIL are not seen by the clang parser since it
+ is relying on code snippets for the values. -->
<rejection class="Qt" field-name="Disambiguated"/>
<rejection class="" enum-name="QCborNegativeInteger"/>
<modify-argument index="1" pyi-type="Optional[PySide6.QtCore.QObject]"/>
</modify-function>
<modify-function signature="connect(const QObject*,const char*,const char*,Qt::ConnectionType)const">
- <modify-argument index="4">
- <rename to="type"/>
- </modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-1"/>
</modify-function>
<!-- static version -->
<modify-function signature="connect(const QObject*,QMetaMethod,const QObject*,QMetaMethod,Qt::ConnectionType)">
- <modify-argument index="5">
- <rename to="type"/>
- </modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-2"/>
</modify-function>
<modify-function signature="connect(const QObject*,const char*,const QObject*,const char*,Qt::ConnectionType)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-3"/>
</modify-function>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect"/>
- <add-function signature="connect(const QObject*,const char*,PyCallable*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+ <add-function signature="connect(const QObject*@sender@,const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
return-type="QMetaObject::Connection" static="yes">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4"/>
</add-function>
+ <add-function signature="connect(const QObject*@sender@,const char*@signal@,const QObject*@context@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
+ return-type="QMetaObject::Connection" static="yes">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4-context"/>
+ </add-function>
<!-- static version -->
- <add-function signature="connect(const char*,PyCallable*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+ <add-function signature="connect(const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
return-type="QMetaObject::Connection">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-5"/>
</add-function>
- <add-function signature="connect(const char*,const QObject*,const char*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+ <add-function signature="connect(const char*@signal@,const QObject*@receiver@,const char*@method@,Qt::ConnectionType@type@=Qt::AutoConnection)"
return-type="QMetaObject::Connection">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-6"/>
</add-function>
<add-function signature="emit(const char*,...)" return-type="bool">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-emit"/>
</add-function>
- <add-function signature="disconnect(const char*,PyCallable*)" return-type="bool">
+ <add-function signature="disconnect(const char*@signal@,PyCallable*@functor@)" return-type="bool">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-disconnect-1"/>
</add-function>
- <add-function signature="disconnect(const QObject*,const char*,PyCallable*)" return-type="bool" static="yes">
+ <add-function signature="disconnect(const QObject*@sender@,const char*@signal@,PyCallable*@functor@)" return-type="bool" static="yes">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-disconnect-2"/>
</add-function>
+ <modify-function signature="disconnect(QMetaObject::Connection)">
+ <modify-argument index="1">
+ <rename to="connection"/>
+ </modify-argument>
+ </modify-function>
<add-function signature="findChild(PyTypeObject*@type@,const QString&@name@={},Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
<suppress-warning text="skipping field 'State::clearFn' with unmatched type 'void'"/>
<suppress-warning text="template baseclass 'QListSpecialMethods<T>' of 'QList' is not known"/>
<suppress-warning text="^.*inherits from a non polymorphic type.*QIODeviceBase.*type discovery based on RTTI is impossible.*$"/>
+ <suppress-warning text="Base class 'QOperatingSystemVersionUnexported' of class 'QOperatingSystemVersion' not found in the type system for setting up inheritance."/>
</typesystem>
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
+ <!-- PYSIDE-2547, hangs -->
+ <modify-function signature="^connect\(.*\)$" allow-thread="yes"/>
</value-type>
<object-type name="QDBusConnectionInterface">
<enum-type name="RegisterServiceReply"/>
</object-type>
<object-type name="QDBusSignature"/>
<object-type name="QDBusUnixFileDescriptor"/>
- <object-type name="QDBusVariant"/>
+ <value-type name="QDBusVariant"/>
<object-type name="QDBusVirtualObject"/>
<suppress-warning text='^.*Unable to translate type "QDBusReply<.*$'/>
<suppress-warning text='^.*QDBusPendingCallWatcher inherits from a non polymorphic type.*$'/>
set(QtGui_SRC_UNITY_EXCLUDED_SRC
${QtGui_GEN_DIR}/qtextframe_iterator_wrapper.cpp
${QtGui_GEN_DIR}/qtextblock_iterator_wrapper.cpp
+ ${QtGui_GEN_DIR}/qshaderversion_wrapper.cpp
)
set_property(SOURCE ${QtGui_SRC_UNITY_EXCLUDED_SRC}
${QtGui_GEN_DIR}/qshader_wrapper.cpp
${QtGui_GEN_DIR}/qshadercode_wrapper.cpp
${QtGui_GEN_DIR}/qshaderkey_wrapper.cpp
-${QtGui_GEN_DIR}/qshaderversion_wrapper.cpp
)
if (ENABLE_WIN)
<include file-name="QSize" location="global"/>
</extra-includes>
<!-- FIXME: PYSIDE7: Handle setQuery(QSqlQuery&&) in some way?
- QTBUG-91766/PYSIDE-2394 -->
+ QTBUG-91766/PYSIDE-2394. allow-thread for PYSIDE-1931 -->
<modify-function signature="setQuery(QSqlQuery)" allow-thread="yes"
deprecated="false"/>
<modify-function signature="setQuery(QString,QSqlDatabase)" allow-thread="yes"/>
<include file-name="QStringList" location="global"/>
<include file-name="QSize" location="global"/>
</extra-includes>
+ <modify-function signature="select()" allow-thread="yes"/> <!-- PYSIDE-1931 -->
</object-type>
<object-type name="QSqlResult">
<enum-type name="BindingSyntax"/>
<include file-name="QStringList" location="global"/>
<include file-name="QSize" location="global"/>
</extra-includes>
+ <modify-function signature="select()" allow-thread="yes"/> <!-- PYSIDE-1931 -->
</object-type>
<object-type name="QSqlDriverCreatorBase">
<extra-includes>
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
// @snippet qobject-connect-4
+// @snippet qobject-connect-4-context
+// %FUNCTION_NAME() - disable generation of function call.
+%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%1, %2, %3, %PYARG_4, %5);
+%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+// @snippet qobject-connect-4-context
+
// @snippet qobject-connect-5
// %FUNCTION_NAME() - disable generation of function call.
%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3);
namespace PySide {
template<> inline Py_ssize_t hash(const QLine &l)
{
- const int v[4] = {l.x1(), l.y1(), l.x2(), l.y2()};
- return qHashRange(v, v + 4);
+ return qHashMulti(0, l.x1(), l.y1(), l.x2(), l.y2());
}
};
// @snippet qline-hash
// @snippet qtimer-singleshot-2
// @snippet qtimer-singleshot-functor-context
-Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
-if (PyObject_TypeCheck(%3, PySideSignalInstance_TypeF())) {
- auto *timerType = Shiboken::SbkType<QTimer>();
- auto *pyTimer = timerType->tp_new(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
- timerType->tp_init(pyTimer, emptyTuple, nullptr);
- QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
- timer->setSingleShot(true);
- PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
- Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
- Shiboken::AutoDecRef result(
- PyObject_CallFunction(PySide::PySideName::qtConnect(), "OsOO",
- pyTimer,
- SIGNAL(timeout()),
- %3,
- PySide::Signal::getObject(signalInstance),
- signalSignature.object())
- );
- timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
- Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
- Py_XDECREF(pyTimer);
- timer->start(%1);
-} else {
+auto msec = %1;
+if (msec == 0) {
+ Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
auto *callable = %PYARG_3;
auto cppCallback = [callable]()
{
};
Py_INCREF(callable);
- %CPPSELF.%FUNCTION_NAME(%1, %2, cppCallback);
+ %CPPSELF.%FUNCTION_NAME(msec, %2, cppCallback);
+} else {
+ Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
+ auto *timerType = Shiboken::SbkType<QTimer>();
+ auto newFunc = timerType->tp_new;
+ auto initFunc = timerType->tp_init;
+ auto *pyTimer = newFunc(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
+ initFunc(pyTimer, emptyTuple, nullptr);
+
+ QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
+ timer->setSingleShot(true);
+
+ Shiboken::AutoDecRef result(
+ PyObject_CallMethod(pyTimer, "connect", "OsOO",
+ pyTimer,
+ SIGNAL(timeout()),
+ %PYARG_2,
+ %PYARG_3)
+ );
+
+ timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
+ Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
+ Py_XDECREF(pyTimer);
+ timer->start(msec);
}
// @snippet qtimer-singleshot-functor-context
// @snippet conversion-pystring-char
char c = %CONVERTTOCPP[char](%in);
-%out = %OUTTYPE(c);
+%out = %OUTTYPE(static_cast<unsigned short>(c));
// @snippet conversion-pystring-char
// @snippet conversion-pyint
return;
}
Shiboken::GilState state;
- PyObject_CallObject(callable, nullptr);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
Py_DECREF(callable);
};
Py_INCREF(callable);
if (arg_qpermission) {
Shiboken::AutoDecRef arglist(PyTuple_New(1));
PyTuple_SET_ITEM(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
- PyObject_CallObject(callable, arglist);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
} else {
- PyObject_CallObject(callable, nullptr);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
}
Py_DECREF(callable);
};
PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- PyObject *key;
- PyObject *value;
- Py_ssize_t pos = 0;
- while (PyDict_Next(ret, &pos, &key, &value)) {
- QString cppKey = %CONVERTTOCPP[QString](key);
- QVariant cppValue = %CONVERTTOCPP[QVariant](value);
- dictPointer->replace(cppKey, cppValue);
+ if (!ret.isNull() && PyDict_Check(ret.object()) != 0) {
+ PyObject *key{};
+ PyObject *value{};
+ Py_ssize_t pos = 0;
+ while (PyDict_Next(ret.object(), &pos, &key, &value)) {
+ QString cppKey = %CONVERTTOCPP[QString](key);
+ QVariant cppValue = %CONVERTTOCPP[QVariant](value);
+ dictPointer->replace(cppKey, cppValue);
+ }
}
Py_DECREF(callable);
// @snippet quiloader-registercustomwidget
registerCustomWidget(%PYARG_1);
-%CPPSELF.addPluginPath(""); // force reload widgets
+%CPPSELF.addPluginPath(QString{}); // force reload widgets
// @snippet quiloader-registercustomwidget
// @snippet quiloader-load-1
QByteArray uiFileName(arg1);
Py_DECREF(strObj);
-QFile uiFile(uiFileName);
+QFile uiFile(QString::fromUtf8(uiFileName));
if (!uiFile.exists()) {
qCritical().noquote() << "File" << uiFileName << "does not exists";
// Use the 'pyside6-uic' wrapper instead of 'uic'
// This approach is better than rely on 'uic' since installing
// the wheels cover this case.
-QString uicBin("pyside6-uic");
+QString uicBin(QStringLiteral("pyside6-uic"));
QStringList uicArgs = {QString::fromUtf8(uiFileName)};
QProcess uicProcess;
PyTuple_SET_ITEM(arglist, 0,
%CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
Py_INCREF(callable);
- PyObject* ret = PyObject_CallObject(callable, arglist);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
Py_DECREF(callable);
- return ret;
-
+ return ret.object() == Py_True;
};
+
%CPPSELF.%FUNCTION_NAME(callback);
// @snippet qwebenginecookiestore-setcookiefilter
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ auto *notification = webEngineNotification.release();
PyTuple_SET_ITEM(arglist.object(), 0,
- Shiboken::Conversions::pointerToPython(
- SbkPySide6_QtWebEngineCoreTypes[SBK_QWEBENGINENOTIFICATION_IDX],
- webEngineNotification.release()));
+ %CONVERTTOPYTHON[QWebEngineNotification*](notification));
Py_INCREF(callable);
- PyObject_CallObject(callable, arglist);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
Py_DECREF(callable);
};
+
%CPPSELF.%FUNCTION_NAME(callback);
// @snippet qwebengineprofile-setnotificationpresenter
PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](text));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
Py_DECREF(callable);
-
};
+
Py_INCREF(callable);
%CPPSELF.%FUNCTION_NAME(callback);
// @snippet qwebenginepage-convertto
// PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](found));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
Py_DECREF(callable);
-
};
+
Py_INCREF(callable);
%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
// @snippet qwebenginepage-runjavascript
PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QByteArray](pdf));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
Py_DECREF(callable);
-
};
+
Py_INCREF(callable);
%CPPSELF.%FUNCTION_NAME(callback, %2);
// @snippet qwebenginepage-printtopdf
int size = Shiboken::String::len(%PYARG_1);
if (size == 1) {
const char *str = Shiboken::String::toCString(%PYARG_1);
- QChar ch(str[0]);
+ const QChar ch(static_cast<unsigned short>(str[0]));
%RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(ch);
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
} else {
# Don't generate the files when cross-building because the target python can not be executed
# on the host machine (usually, unless you use some userspace qemu based mechanism).
# TODO: Can we do something better here to still get pyi files?
- if(NOT PYSIDE_IS_CROSS_BUILD)
+ if(NOT (PYSIDE_IS_CROSS_BUILD OR DISABLE_PYI))
set(generate_pyi_options ${module_NAME} --sys-path
"${pysidebindings_BINARY_DIR}"
"${SHIBOKEN_PYTHON_MODULE_DIR}/..") # use the layer above shiboken6
pyside_internal_find_host_shiboken_tools()
find_package(Shiboken6 6 CONFIG REQUIRED)
+if(is_pyside6_superproject_build)
+ shiboken_find_required_python()
+endif()
+
set(BINDING_API_MAJOR_VERSION "${pyside_MAJOR_VERSION}")
set(BINDING_API_MINOR_VERSION "${pyside_MINOR_VERSION}")
set(BINDING_API_MICRO_VERSION "${pyside_MICRO_VERSION}")
# Detect if the Python interpreter is actually PyPy
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+ COMMAND ${Python_EXECUTABLE} -c "if True:
pypy_version = ''
import sys
if hasattr(sys, 'pypy_version_info'):
# for a rst_build_docs case, and not a full doc build
if (NOT FULLDOCSBUILD)
find_package(Python COMPONENTS Interpreter)
- set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
endif()
if (QT_SRC_DIR)
if(PYSIDE_IS_CROSS_BUILD)
set(python_executable "${QFP_PYTHON_HOST_PATH}")
else()
- set(python_executable "${PYTHON_EXECUTABLE}")
+ set(python_executable "${Python_EXECUTABLE}")
endif()
set(TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../tools")
file(WRITE ${global_typesystem} "${typeSystemDocXmlContents}")
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} "${TOOLS_DIR}/doc_modules.py"
+ COMMAND ${Python_EXECUTABLE} "${TOOLS_DIR}/doc_modules.py"
-t "${global_typesystem}" -g "${global_header}" -d "${config_docconf}"
"${QT_INCLUDE_DIR}" "${SUPPORTED_QT_VERSION}"
OUTPUT_VARIABLE ALL_DOC_MODULES
file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/${qtdoc_doc_conf}\n")
configure_file("pyside.qdocconf.in" "pyside.qdocconf" @ONLY)
+ configure_file("qdoc_spawner.py.in" "qdoc_spawner.py" @ONLY)
set(QDOC_TYPESYSTEM_PATH "${pyside6_SOURCE_DIR}${PATH_SEP}${pyside6_BINARY_DIR}")
add_custom_target(qdoc DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml")
add_custom_command(OUTPUT "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
- # Use dummy Qt version information, QDoc needs it but has no effect on WebXML output
- COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc
- QT_VERSION=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
- QT_VER=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}
- QT_VERSION_TAG=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
- "${qdoc_binary}" pyside.qdocconf -single-exec
- -installdir ${DOC_DATA_DIR} -outputdir ${DOC_DATA_DIR}
+ Use dummy Qt version information, QDoc needs it but has no effect on WebXML output
+ COMMAND ${CMAKE_COMMAND} -E env ${python_executable} qdoc_spawner.py
+ --qt=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
+ --doc-data-dir=${DOC_DATA_DIR}
+ --qdoc-binary=${qdoc_binary}
+ --build-dir=${CMAKE_CURRENT_LIST_DIR}/src
+ --qt-install-docs=${QT_SRC_DIR}/doc
+ --parallel="auto"
+ --verbose
COMMENT "Running qdoc against Qt source code...")
endif()
+# Avoid using 'auto' for '-j' option as it could lead to
+# crash on some systems due to out-of-memory situation. Instead
+# use a no. less than 8.
add_custom_target(apidoc
COMMAND ${CMAKE_COMMAND} -E env INHERITANCE_FILE=${ENV_INHERITANCE_FILE}
${SHIBOKEN_PYTHON_INTERPRETER} ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT}
- -j auto ${CMAKE_CURRENT_BINARY_DIR}/rst html
+ -j 6 ${CMAKE_CURRENT_BINARY_DIR}/rst html
COMMENT "Generating PySide htmls..."
)
${CMAKE_CURRENT_BINARY_DIR}/../../shiboken6/doc/html
${CMAKE_CURRENT_BINARY_DIR}/html/shiboken6
COMMENT "Copying Shiboken docs..."
+ DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
VERBATIM)
else()
if(qhelpgenerator_binary)
COMMAND ${python_executable} ${PATCH_QHP_SCRIPT} -p -v pyside6 ${QHP_FILE}
COMMAND "${qhelpgenerator_binary}" ${QHP_FILE}
COMMENT "Generating QCH from a QHP file..."
+ DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
VERBATIM
)
else()
--- /dev/null
+.. module:: PySide6.QtAsyncio
+
+PySide6.QtAsyncio
+*****************
+
+.. note:: This module is currently in technical preview.
+
+The Qt Asyncio module is a pure Python module that allows programs to be
+written that use Qt's API in conjunction with `asyncio
+<https://docs.python.org/3/library/asyncio.html>`_. asyncio is a popular
+Python library for asynchronous programming. It is used in particular
+for programs that need to handle many I/O operations from many sources,
+such as web servers. More generally, it allows developers to work with
+`couroutines <https://docs.python.org/3/library/asyncio-task.html#coroutine>`_.
+Coroutines can be imagined as "asynchronous functions". In contrast to
+Qt's signals and slot mechanism, this allows for asynchronous programs
+that are closer in program flow to synchronous programs, as programs no
+longer have to be imagined as a series of callbacks. Instead, coroutines
+transparently resume and yield at designated spots.
+
+Consider the following simple coroutine defined with the ``async``
+keyword in front of its definition:
+
+::
+
+ async def do_something():
+ result = await do_something_asynchronously()
+ print(result)
+
+``do_something_asynchronously()`` is a coroutine itself, e.g., an
+I/O-heavy operation that would normally block the execution flow in a
+synchronous program. Instead, the ``await`` keyword is used to wait for
+the result, at which point ``do_something()`` yields and the program
+flow transparently switches to the next asynchronous task. When the
+result becomes available, the program flow is able to switch back to the
+``do_something()`` coroutine, which then resumes and prints the result.
+
+The asyncio API
+^^^^^^^^^^^^^^^
+
+asyncio and Qt are both based on an event loop. asyncio provides an API
+to replace its default event loop with a custom implementation.
+**QtAsyncio** provides such an implementation that uses Qt's event loop,
+allowing Qt and asyncio to be used together.
+
+We consider that this API consists of two levels:
+
+1. Fundamental infrastructure for event loops and asynchronous
+ operations, including `futures
+ <https://docs.python.org/3/library/asyncio-future.html#asyncio.Future>`_,
+ `tasks <https://docs.python.org/3/library/asyncio-task.html#asyncio.Task>`_,
+ `handles <https://docs.python.org/3/library/asyncio-eventloop.html#callback-handles>`_,
+ executors, and event loop management functions (see below).
+2. A user-facing API for use in applications, including transports and
+ protocols, network connections, servers, sockets, signals,
+ subprocesses.
+
+**QtAsyncio** currently covers the first level. This includes the
+following functions, for which the API is identical with QtAsyncio as
+with asyncio:
+
+* `run_until_complete() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_until_complete>`_
+* `run_forever() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_forever>`_
+* `stop() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.stop>`_
+* `is_running() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.is_running>`_
+* `is_closed() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.is_closed>`_
+* `close() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.close>`_
+* `shutdown_asyncgens() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_asyncgens>`_
+* `shutdown_default_executor() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_default_executor>`_
+* `call_soon() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon>`_
+* `call_soon_threadsafe() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon_threadsafe>`_
+* `call_later() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later>`_
+* `call_at() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_at>`_
+* `time() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.time>`_
+* `create_future() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_future>`_
+* `create_task() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_task>`_
+* `set_task_factory() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.set_task_factory>`_
+* `get_task_factory() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.get_task_factory>`_
+
+Also included is the ability to
+`run synchronous code in an executor <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor>`_
+(``ThreadPoolExecutor``).
+
+Get started with QtAsyncio
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To write a program with **QtAsyncio**, first import the module, e.g.:
+
+::
+
+ import PySide6.QtAsyncio as QtAsyncio
+
+**QtAsyncio** provides a function ``run()`` that can be used to run a
+specific coroutine until it is complete, or to start the Qt & asyncio
+event loop plainly. The former case makes sense if the program flow
+starts with said coroutine, the latter case makes sense if a coroutine
+is enqueued later in the program flow, e.g., after pressing a button in
+the UI.
+
+::
+
+ QtAsyncio.run()
+
+(see the `asyncio "minimal" example <https://doc.qt.io/qtforpython-6/examples/example_async_minimal.html>`_
+for an instance of this usage) or
+
+::
+
+ QtAsyncio.run(my_coroutine())
+
+(see the `asyncio "Eratosthenes" example <https://doc.qt.io/qtforpython-6/examples/example_async_eratosthenes.html>`_)
+or
+
+::
+
+ QtAsyncio.run(my_coroutine(), keep_running=False)
+
+to run the coroutine and then stop the event loop upon its completion.
+This latter case behaves identically to ``asyncio.run(my_coroutine())``.
+
+An additional optional argument ``quit_qapp`` can be passed to ``run()``
+to configure whether the QCoreApplication at the core of QtAsyncio
+should be shut down when asyncio finishes. A special case where one
+would want to disable this is test suites that want to reuse a single
+QCoreApplication instance across all unit tests, which would fail if
+this instance is shut down every time. The default is ``True``.
+
+Note that this argument is orthogonal to the ``keep_running`` argument.
+``keep_running`` determines if **asyncio** should keep running after the
+coroutine has finished, while ``quit_qapp`` determines if the
+QCoreApplication should be shut down after asyncio has finished. It is
+possible for asyncio to finish while the QCoreApplication is kept alive.
+
+Coroutines explained
+^^^^^^^^^^^^^^^^^^^^
+
+Coroutines are functions that can be paused (yield) and resumed. Behind
+this simple concept lies a complex mechanism that is abstracted by the
+asynchronous framework. This talk presents a diagram that attempts to
+illustrate the flow of a coroutine from the moment it's provided to the
+async framework until it's completed.
+
+.. image:: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
+ :alt: Asynchronous programming with asyncio and Qt
+ :target: https://www.youtube.com/watch?v=XuqdTvisqkQ
custom-types.webxml
database.webxml
datastreamformat.webxml
-datavisualization-examples.webxml
dbus-changes-qt6.webxml
determining-the-default-sensor-for-a-type.webxml
dialogs.webxml
qml-tutorial2.webxml
qml-tutorial3.webxml
qmldiskcache.webxml
-qmlexampletoggleswitch.webxml
qmlreference.webxml
qndeffilter-record.webxml
qoutputrange.webxml
qsgmaterialshader-graphicspipelinestate.webxml
qsgmaterialtype.webxml
qstaticplugin.webxml
-qt3d-advancedcustommaterial-example.webxml
-qt3d-audio-visualizer-qml-example.webxml
qt3d-basicshapes-cpp-example.webxml
qt3d-changes-qt6.webxml
qt3d-cpp.webxml
qt3d-multiviewport-example.webxml
qt3d-overview.webxml
qt3d-pbr-materials-example.webxml
-qt3d-planets-qml-example.webxml
qt3d-qml.webxml
-qt3d-scene2d-example.webxml
-qt3d-scene3d-example.webxml
-qt3d-shadow-map-qml-example.webxml
qt3d-simple-qml-example.webxml
qt3d-simplecustommaterial-example.webxml
-qt3d-wave-example.webxml
-qt3d-widgets-scene3d-example.webxml
qt3d-wireframe-example.webxml
qt3drender-framegraph.webxml
qt3drender-geometry.webxml
qtbluetooth-le-overview.webxml
qtbluetooth-lowenergyscanner-example.webxml
qtbluetooth-overview.webxml
-qtbluetooth-pingpong-example.webxml
qtcborcommon.webxml
qtcharts-barmodelmapper-example.webxml
qtcharts-boxplotchart-example.webxml
qtcharts-changes-qt6.webxml
qtcharts-customchart-example.webxml
qtcharts-datetimeaxis-example.webxml
-qtcharts-donutchart-example.webxml
qtcharts-examples.webxml
qtcharts-horizontalbarchart-example.webxml
qtcharts-horizontalpercentbarchart-example.webxml
qtcharts-multiaxis-example.webxml
qtcharts-openglseries-example.webxml
qtcharts-overview.webxml
-qtcharts-piechartcustomization-example.webxml
-qtcharts-piechartdrilldown-example.webxml
qtcharts-pointsselectionandmarkers-example.webxml
qtcharts-polarchart-example.webxml
qtcharts-qmlaxes-example.webxml
-qtcharts-qmlchart-example.webxml
qtcharts-qmlcustomizations-example.webxml
qtcharts-qmlcustomlegend-example.webxml
qtcharts-qmlf1legends-example.webxml
qtcharts-qmloscilloscope-example.webxml
qtcharts-qmlweather-example.webxml
qtcharts-scatterchart-example.webxml
-qtcharts-scatterinteractions-example.webxml
qtcharts-selectedbar-example.webxml
qtcharts-splinechart-example.webxml
qtcharts-stackedbarchart-example.webxml
-qtcharts-stackedbarchartdrilldown-example.webxml
qtcharts-zoomlinechart-example.webxml
qtconcurrent-imagescaling-example.webxml
-qtconcurrent-map-example.webxml
-qtconcurrent-progressdialog-example.webxml
-qtconcurrent-runfunction-example.webxml
qtconcurrent-wordcount-example.webxml
qtconcurrentfilter.webxml
qtconcurrentmap.webxml
qtcore-threads-semaphores-example.webxml
qtcore-threads-waitconditions-example.webxml
qtcore-tools-contiguouscache-example.webxml
-qtcore-tools-customtype-example.webxml
-qtdatavis3d-custominput-example.webxml
-qtdatavis3d-customitems-example.webxml
-qtdatavis3d-customproxy-example.webxml
-qtdatavis3d-draggableaxes-example.webxml
-qtdatavis3d-itemmodel-example.webxml
-qtdatavis3d-qml3doscilloscope-example.webxml
-qtdatavis3d-qmlaxisdrag-example.webxml
-qtdatavis3d-qmlaxisformatter-example.webxml
-qtdatavis3d-qmlbars-example.webxml
-qtdatavis3d-qmlcustominput-example.webxml
-qtdatavis3d-qmllegend-example.webxml
-qtdatavis3d-qmlmultigraph-example.webxml
-qtdatavis3d-qmlscatter-example.webxml
-qtdatavis3d-qmlspectrogram-example.webxml
-qtdatavis3d-qmlsurface-example.webxml
-qtdatavis3d-qmlsurfacelayers-example.webxml
-qtdatavis3d-rotations-example.webxml
-qtdatavis3d-scatter-example.webxml
-qtdatavis3d-texturesurface-example.webxml
-qtdatavis3d-volumetric-example.webxml
-qtdatavisualization-data-handling.webxml
-qtdatavisualization-interacting-with-data.webxml
-qtdatavisualization-known-issues.webxml
-qtdatavisualization-overview.webxml
qtdbus-chat-example.webxml
qtdbus-cmake-qt-add-dbus-adaptor.webxml
qtdbus-cmake-qt-add-dbus-interface.webxml
qtdesigner-containerextension-example.webxml
qtdesigner-customwidgetplugin-example.webxml
qtdesigner-manual.webxml
-qtdesigner-worldtimeclockbuilder-example.webxml
-qtdesigner-worldtimeclockplugin-example.webxml
qtest-overview.webxml
qtest-tutorial.webxml
qtextedit-extraselection.webxml
qtmath.webxml
qtmultimedia-apple.webxml
qtmultimedia-changes-qt6.webxml
-qtmultimedia-multimedia-audiodevices-example.webxml
-qtmultimedia-multimedia-audiorecorder-example.webxml
-qtmultimedia-multimedia-declarative-camera-example.webxml
-qtmultimedia-multimedia-spectrum-example.webxml
-qtmultimedia-multimedia-video-mediaplayer-example.webxml
-qtmultimedia-multimedia-video-qmlvideo-example.webxml
-qtmultimedia-multimedia-video-recorder-example.webxml
-qtmultimedia-multimediawidgets-videographicsitem-example.webxml
-qtmultimedia-multimediawidgets-videowidget-example.webxml
qtnetwork-broadcastreceiver-example.webxml
qtnetwork-broadcastsender-example.webxml
-qtnetwork-downloadmanager-example.webxml
qtnetwork-http-example.webxml
qtnetwork-multicastreceiver-example.webxml
qtnetwork-multicastsender-example.webxml
qtnetwork-secureudpclient-example.webxml
qtnetwork-secureudpserver-example.webxml
qtnetwork-torrent-example.webxml
-qtnetworkauth-changes-qt6.webxml
-qtnetworkauth-twittertimeline-example.webxml
qtnfc-annotatedurl-example.webxml
qtnfc-changes-qt6.webxml
qtnfc-ndefeditor-example.webxml
qtpositioning-android.webxml
qtpositioning-changes-qt6.webxml
qtpositioning-examples.webxml
-qtpositioning-geoflickr-example.webxml
qtpositioning-ios.webxml
qtpositioning-logfilepositionsource-example.webxml
qtpositioning-plugins.webxml
qtqml-javascript-qmlglobalobject.webxml
qtqml-javascript-resources.webxml
qtqml-javascript-topic.webxml
-qtqml-networkaccessmanagerfactory-example.webxml
qtqml-qml-i18n-example.webxml
-qtqml-qmlextensionplugins-example.webxml
qtqml-syntax-basics.webxml
qtqml-syntax-directoryimports.webxml
qtqml-syntax-imports.webxml
qtqml-syntax-objectattributes.webxml
qtqml-syntax-propertybinding.webxml
-qtqml-tool-qmltc.webxml
qtqml-tutorials-extending-qml-example.webxml
qtqml-typesystem-basictypes.webxml
qtqml-typesystem-objecttypes.webxml
qtqml-typesystem-topic.webxml
-qtqml-xmlhttprequest-example.webxml
qtquick-animation-example.webxml
qtquick-bestpractices.webxml
qtquick-canvas-example.webxml
qtquick-customitems-dialcontrol-example.webxml
qtquick-customitems-flipable-example.webxml
qtquick-customitems-painteditem-example.webxml
-qtquick-customitems-scrollbar-example.webxml
-qtquick-customitems-tabwidget-example.webxml
qtquick-draganddrop-example.webxml
qtquick-effects-particles.webxml
qtquick-effects-sprites.webxml
qtquick-effects-topic.webxml
qtquick-effects-transformations.webxml
-qtquick-externaldraganddrop-example.webxml
qtquick-imageelements-example.webxml
qtquick-imageprovider-example.webxml
qtquick-imageresponseprovider-example.webxml
qtquick-positioning-topic.webxml
qtquick-quick-accessibility-example.webxml
qtquick-quickwidgets-quickwidget-example.webxml
-qtquick-righttoleft-example.webxml
qtquick-scenegraph-custommaterial-example.webxml
qtquick-scenegraph-d3d11underqml-example.webxml
-qtquick-scenegraph-fboitem-example.webxml
qtquick-scenegraph-graph-example.webxml
qtquick-scenegraph-materials.webxml
qtquick-scenegraph-metaltextureimport-example.webxml
qtquick-tableview-gameoflife-example.webxml
qtquick-tableview-pixelator-example.webxml
qtquick-text-example.webxml
-qtquick-text-validator.webxml
-qtquick-threading-example.webxml
-qtquick-threading-threadedlistmodel-example.webxml
qtquick-tool-qmllint.webxml
qtquick-tools-and-utilities.webxml
-qtquick-touchinteraction-example.webxml
qtquick-tutorials-dynamicview-dynamicview1-example.webxml
qtquick-tutorials-dynamicview-dynamicview2-example.webxml
qtquick-tutorials-dynamicview-dynamicview3-example.webxml
qtquickcontrols-eventcalendar-example.webxml
qtquickcontrols-flatstyle-example.webxml
qtquickcontrols-imagine-automotive-example.webxml
-qtquickcontrols-imagine-musicplayer-example.webxml
-qtquickcontrols-sidepanel-example.webxml
-qtquickcontrols-swipetoremove-example.webxml
qtquickcontrols-texteditor-example.webxml
qtquickcontrols-wearable-example.webxml
-qtquickcontrols2-basic.webxml
-qtquickcontrols2-buttons.webxml
-qtquickcontrols2-configuration.webxml
-qtquickcontrols2-containers.webxml
-qtquickcontrols2-customize.webxml
-qtquickcontrols2-delegates.webxml
-qtquickcontrols2-deployment.webxml
-qtquickcontrols2-environment.webxml
-qtquickcontrols2-examples.webxml
-qtquickcontrols2-fileselectors.webxml
-qtquickcontrols2-focus.webxml
-qtquickcontrols2-fusion.webxml
-qtquickcontrols2-gettingstarted.webxml
-qtquickcontrols2-guidelines.webxml
-qtquickcontrols2-icons.webxml
-qtquickcontrols2-imagine.webxml
-qtquickcontrols2-indicators.webxml
-qtquickcontrols2-input.webxml
-qtquickcontrols2-macos.webxml
-qtquickcontrols2-material.webxml
-qtquickcontrols2-menus.webxml
-qtquickcontrols2-navigation.webxml
-qtquickcontrols2-popups.webxml
-qtquickcontrols2-separators.webxml
-qtquickcontrols2-styles.webxml
-qtquickcontrols2-universal.webxml
-qtquickcontrols2-windows.webxml
+qtquickcontrols-basic.webxml
+qtquickcontrols-buttons.webxml
+qtquickcontrols-configuration.webxml
+qtquickcontrols-containers.webxml
+qtquickcontrols-customize.webxml
+qtquickcontrols-delegates.webxml
+qtquickcontrols-deployment.webxml
+qtquickcontrols-environment.webxml
+qtquickcontrols-examples.webxml
+qtquickcontrols-fileselectors.webxml
+qtquickcontrols-focus.webxml
+qtquickcontrols-fusion.webxml
+qtquickcontrols-gettingstarted.webxml
+qtquickcontrols-guidelines.webxml
+qtquickcontrols-icons.webxml
+qtquickcontrols-imagine.webxml
+qtquickcontrols-indicators.webxml
+qtquickcontrols-input.webxml
+qtquickcontrols-macos.webxml
+qtquickcontrols-material.webxml
+qtquickcontrols-menus.webxml
+qtquickcontrols-navigation.webxml
+qtquickcontrols-popups.webxml
+qtquickcontrols-separators.webxml
+qtquickcontrols-styles.webxml
+qtquickcontrols-universal.webxml
+qtquickcontrols-windows.webxml
qtquicklayouts-overview.webxml
qtremoteobjects-cmake-qt-add-repc-merged.webxml
qtremoteobjects-cmake-qt-add-repc-replicas.webxml
qtremoteobjects-gettingstarted.webxml
qtremoteobjects-interaction.webxml
qtremoteobjects-node.webxml
-qtremoteobjects-qmlmodelviewclient-example.webxml
qtremoteobjects-registry.webxml
qtremoteobjects-repc.webxml
qtremoteobjects-replica.webxml
qtremoteobjects-ssl-example.webxml
qtremoteobjects-troubleshooting.webxml
qtremoteobjects-websockets-example.webxml
-qtscxml-calculator-qml-example.webxml
-qtscxml-calculator-widgets-example.webxml
qtscxml-changes-qt6.webxml
qtscxml-cmake-qt-add-statecharts.webxml
qtscxml-ftpclient-example.webxml
qtscxml-instantiating-state-machines.webxml
-qtscxml-invoke-dynamic-example.webxml
-qtscxml-invoke-static-example.webxml
-qtscxml-mediaplayer-qml-cppdatamodel-example.webxml
-qtscxml-mediaplayer-qml-dynamic-example.webxml
-qtscxml-mediaplayer-qml-static-example.webxml
-qtscxml-mediaplayer-widgets-dynamic-example.webxml
-qtscxml-mediaplayer-widgets-static-example.webxml
qtscxml-overview.webxml
-qtscxml-pinball-example.webxml
qtscxml-scxml-compliance.webxml
qtscxml-sudoku-example.webxml
qtscxml-trafficlight-qml-dynamic-example.webxml
qtscxml-trafficlight-qml-static-example.webxml
qtscxml-trafficlight-widgets-dynamic-example.webxml
qtscxml-trafficlight-widgets-static-example.webxml
-qtsensors-accelbubble-example.webxml
qtsensors-changes-qt6.webxml
qtsensors-cpp.webxml
qtsensors-examples.webxml
-qtsensors-grue-example.webxml
-qtsensors-maze-example.webxml
-qtsensors-qmlqtsensors-example.webxml
-qtsensors-sensor-explorer-example.webxml
qtsensors-sensorsshowcase-example.webxml
qtserialport-blockingreceiver-example.webxml
qtserialport-blockingsender-example.webxml
-qtserialport-cenumerator-example.webxml
qtserialport-changes-qt6.webxml
-qtserialport-creaderasync-example.webxml
-qtserialport-creadersync-example.webxml
-qtserialport-cwriterasync-example.webxml
-qtserialport-cwritersync-example.webxml
-qtserialport-enumerator-example.webxml
qtserialport-examples.webxml
qtserialport-terminal-example.webxml
qtsql-cachedtable-example.webxml
qtsql-sqlwidgetmapper-example.webxml
qtsql-tablemodel-example.webxml
qtsvg-changes-qt6.webxml
-qtsvg-richtext-textobject-example.webxml
-qtsvg-svggenerator-example.webxml
-qtsvg-svgviewer-example.webxml
qttest-best-practices-qdoc.webxml
qttestlib-tutorial1-example.webxml
qttestlib-tutorial2-example.webxml
qttestlib-tutorial4-example.webxml
qttestlib-tutorial5-example.webxml
qttestlib-tutorial6.webxml
-qtuitools-multipleinheritance-example.webxml
qtuitools-textfinder-example.webxml
qtwebchannel-changes-qt6.webxml
qtwebchannel-chatclient-html-example.webxml
qtwebengine-features.webxml
qtwebengine-overview.webxml
qtwebengine-platform-notes.webxml
-qtwebengine-webenginequick-customdialogs-example.webxml
qtwebengine-webenginequick-lifecycle-example.webxml
-qtwebengine-webenginequick-minimal-example.webxml
-qtwebengine-webenginequick-recipebrowser-example.webxml
-qtwebengine-webenginequick-webengineaction-example.webxml
qtwebengine-webenginewidgets-contentmanipulation-example.webxml
qtwebengine-webenginewidgets-cookiebrowser-example.webxml
qtwebengine-webenginewidgets-html2pdf-example.webxml
qtwebengine-webenginewidgets-maps-example.webxml
-qtwebengine-webenginewidgets-minimal-example.webxml
qtwebengine-webenginewidgets-printme-example.webxml
qtwebengine-webenginewidgets-spellchecker-example.webxml
-qtwebengine-webenginewidgets-stylesheetbrowser-example.webxml
qtwebengine-webenginewidgets-videoplayer-example.webxml
-qtwebengine-webenginewidgets-webui-example.webxml
qtwebenginewidgets-qtwebkitportingguide.webxml
qtwebsockets-changes-qt6.webxml
qtwebsockets-echoclient-example.webxml
qtwebsockets-sslechoclient-example.webxml
qtwebsockets-sslechoserver-example.webxml
qtwebsockets-testing.webxml
-qtwidgets-draganddrop-fridgemagnets-example.webxml
-qtwidgets-draganddrop-puzzle-example.webxml
-qtwidgets-effects-fademessage-example.webxml
qtwidgets-gestures-imagegestures-example.webxml
qtwidgets-graphicsview-basicgraphicslayouts-example.webxml
qtwidgets-graphicsview-chip-example.webxml
-qtwidgets-graphicsview-embeddeddialogs-example.webxml
-qtwidgets-graphicsview-flowlayout-example.webxml
qtwidgets-graphicsview-simpleanchorlayout-example.webxml
-qtwidgets-graphicsview-weatheranchorlayout-example.webxml
-qtwidgets-itemviews-chart-example.webxml
qtwidgets-itemviews-coloreditorfactory-example.webxml
qtwidgets-itemviews-combowidgetmapper-example.webxml
qtwidgets-itemviews-customsortfiltermodel-example.webxml
-qtwidgets-itemviews-dirview-example.webxml
qtwidgets-itemviews-frozencolumn-example.webxml
-qtwidgets-itemviews-interview-example.webxml
-qtwidgets-itemviews-pixelator-example.webxml
-qtwidgets-itemviews-puzzle-example.webxml
-qtwidgets-itemviews-simpledommodel-example.webxml
qtwidgets-itemviews-simpletreemodel-example.webxml
-qtwidgets-itemviews-simplewidgetmapper-example.webxml
-qtwidgets-mainwindows-mainwindow-example.webxml
qtwidgets-mainwindows-menus-example.webxml
-qtwidgets-mainwindows-sdi-example.webxml
qtwidgets-painting-affine-example.webxml
qtwidgets-painting-composition-example.webxml
qtwidgets-painting-deform-example.webxml
-qtwidgets-painting-fontsampler-example.webxml
qtwidgets-painting-gradients-example.webxml
qtwidgets-painting-imagecomposition-example.webxml
qtwidgets-painting-painterpaths-example.webxml
qtwidgets-painting-pathstroke-example.webxml
qtwidgets-painting-transformations-example.webxml
-qtwidgets-richtext-calendar-example.webxml
qtwidgets-tools-completer-example.webxml
qtwidgets-tools-customcompleter-example.webxml
qtwidgets-tools-echoplugin-example.webxml
-qtwidgets-tools-i18n-example.webxml
-qtwidgets-tools-plugandpaint-app-example.webxml
-qtwidgets-tools-plugandpaint-plugins-basictools-example.webxml
-qtwidgets-tools-plugandpaint-plugins-extrafilters-example.webxml
qtwidgets-tools-settingseditor-example.webxml
qtwidgets-tools-styleplugin-example.webxml
qtwidgets-tools-treemodelcompleter-example.webxml
-qtwidgets-tools-undo-example.webxml
qtwidgets-tools-undoframework-example.webxml
-qtwidgets-touch-dials-example.webxml
-qtwidgets-touch-fingerpaint-example.webxml
qtwidgets-touch-knobs-example.webxml
-qtwidgets-touch-pinchzoom-example.webxml
qtwidgets-tutorials-notepad-example.webxml
qtwidgets-tutorials-widgets-childwidget-example.webxml
qtwidgets-tutorials-widgets-nestedlayouts-example.webxml
qtwidgets-widgets-analogclock-example.webxml
qtwidgets-widgets-calculator-example.webxml
qtwidgets-widgets-calendarwidget-example.webxml
-qtwidgets-widgets-digitalclock-example.webxml
-qtwidgets-widgets-elidedlabel-example.webxml
qtwidgets-widgets-groupbox-example.webxml
-qtwidgets-widgets-icons-example.webxml
-qtwidgets-widgets-imageviewer-example.webxml
qtwidgets-widgets-lineedits-example.webxml
-qtwidgets-widgets-mousebuttons-example.webxml
-qtwidgets-widgets-movie-example.webxml
qtwidgets-widgets-scribble-example.webxml
qtwidgets-widgets-shapedclock-example.webxml
qtwidgets-widgets-sliders-example.webxml
qtwidgets-widgets-spinboxes-example.webxml
-qtwidgets-widgets-styles-example.webxml
-qtwidgets-widgets-stylesheet-example.webxml
qtwidgets-widgets-tablet-example.webxml
-qtwidgets-widgets-tooltips-example.webxml
-qtwidgets-widgets-validators-example.webxml
-qtwidgets-widgets-wiggly-example.webxml
qtwidgets-widgets-windowflags-example.webxml
-qtxml-streambookmarks-example.webxml
-qtxml-xmlstreamlint-example.webxml
quick-changes-qt6.webxml
qwebenginecookiestore-filterrequest.webxml
qwidget-styling.webxml
stylesheet.webxml
svgrendering.webxml
testlib-changes-qt6.webxml
-textedit-example.webxml
timers.webxml
-tutorials-addressbook.webxml
usingadaptors.webxml
videooverview.webxml
webengine-examples.webxml
# qtdoc repository
accessible.webxml
appicon.webxml
-connectivity.webxml
create-your-first-applications.webxml
deployment.webxml
desktop-integration.webxml
explore-qt.webxml
get-and-install-qt.webxml
gettingstarted.webxml
-gettingstartedqml.webxml
highdpi.webxml
install-qt-design-studio.webxml
ipc.webxml
rcc.webxml
restoring-geometry.webxml
scalability.webxml
-scripting.webxml
session.webxml
sharedlibrary.webxml
solutions-for-application-development.webxml
topics-ui.webxml
uic.webxml
unicode.webxml
-unix-signal-handlers.webxml
wayland-and-qt.webxml
.. _commercial-page:
-Commercial Distribution
-=======================
+Commercial Use
+==============
|project| follows the same licensing that Qt has, which means that there are two
distributions, the Community Edition (LGPLv3/GPLv3) and a Commercial Edition. For
``Qt OPC UA``, ``Qt MQTT`` and ``Qt CoAP`` modules, which are distributed in
a special Python wheel.
-All commercial licenses include the 5.15.x and 6.2.x LTS releases.
+|project| follows the same approach as Qt, meaning that commercial
+users will have access to both our commercial packages for any
+given version, or the special commercial LTS releases.
Commercial users **should not** install the Community Edition distribution via ``pip
install pyside6`` to avoid licensing problems, and should refer to the
-packages provided in the `Qt Account`_.
+packages that can be acquired from the `Qt Account`_, the Qt Installer, or
+via the `qtpip` tool.
Installation
------------
'sphinx.ext.coverage', 'sphinx.ext.intersphinx', 'sphinx.ext.todo',
'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
'sphinx.ext.viewcode',
- 'sphinx_design', 'sphinx_copybutton', 'myst_parser']
+ 'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',]
myst_enable_extensions = [
"amsmath",
# General information about the project.
project = u'PySide'
-copyright = u'2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
+copyright = u'2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
+ # FIXME: This option is currently enable because on the 'requirements-doc.txt'
+ # We are using a fork of the theme, to include this new option.
+ # This needs to be removed once the functionality is either upstreamed,
+ # or a similar option is provided.
+ "collapse_navbar": True,
"dark_css_variables": {
"color-brand-primary": "#2cde85",
"color-brand-content": "#2cde85",
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+html_favicon = "_static/qtforpython.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# -- Options for qthelp output ---------------------------------------------------
qthelp_theme = 'pysidedocs_qthelp'
+
+# for example tagging based on supported platforms
+tags_create_tags = True
+tags_create_badges = True
+tags_badge_colors = {
+ "Android": "info",
+}
:maxdepth: 2
quickstart.rst
+ commercial/index.rst
gettingstarted/index.rst
api.rst
tutorials/index.rst
deployment/index.rst
considerations.rst
developer/index.rst
- commercial/index.rst
..
Intersphinx references in toctrees is not supported
--- /dev/null
+.. _developer-adapt-qt:
+
+Adapt to new Qt versions
+========================
+
+The dev branch of PySide is switched to a new Qt minor version
+after its API review is finished and the API is stable.
+
+Until that happens, a patch should be continuously developed
+to adapt to this version.
+
+The `new classes page <https://doc-snapshots.qt.io/qt6-6.7/newclasses67.html>`_
+is a good source of information for new API.
+
+New classes and should be added to the type system file (using
+a ``since`` attribute) and ``CMakeList.txt`` file of the respective module.
+
+Should the class not be available on all platforms, the respective
+``QT_CONFIG`` macro needs to be specified in the type system file and
+feature checks need to be added to ``CMakeList.txt`` (see for example
+``QPermission``).
+
+The process consists of running a build and evaluating the log file.
+The script
+`shiboken2tasks.py <https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/scripts/shiboken2tasks.py>`_
+from the Qt Creator repository can be used to convert the shiboken warnings
+into a `task file <https://doc.qt.io/qtcreator/creator-task-lists.html>`_
+for display in the build issues pane of Qt Creator.
+
+Warnings about new enumerations will be shown there; they should be added
+to type system file using a ``since`` attribute.
+
+Warnings about not finding a function signature for modification
+also need to be handled; mostly this is a sign of a function parameter
+being changed from ``int`` to ``qsizetype`` or similar.
+
+If the build succeeds, a test run should be done.
+
+The Qt source code should be checked for new overloads
+(indicated by ``QT6_DECL_NEW_OVERLOAD_TAIL`` starting from 6.7).
+The resolution needs to be decided for each individual case,
+mostly by removing old functions and using ``<declare-function>``
+to declare new API.
of using ``#include <QtModule/header.h>`` since module include paths
are not passed in PySide.
+Distribution
+------------
+
+- Determine to which wheel the module belongs according to
+ `Qt Modules <https://doc.qt.io/qt-6/qtmodules.html>`_.
+- Add the module to ``build_scripts/wheel_files.py`` for use by
+ ``create_wheels.py``.
+- Add the module to one of the ``README.pyside6_*.md`` files.
+
Add documentation
-----------------
application, or simply you can port an existing Qt example that does not have
a Python counterpart.
-For both cases, we recommend you to use tools like
-`flake8 <https://pypi.org/project/flake8/>`_
-(or `ruff <https://pypi.org/project/ruff/>`_) to detect issues with your code.
+Example code should be free of `flake8 <https://pypi.org/project/flake8/>`_
+warnings; this is enforced by a bot. A configuration file is provided
+at the root of the repository. Offending lines can be excluded by a
+``noqa`` directive if there is a good reason to do so.
+
Keep in mind we do allow 100 columns for line length.
+
Additionally, please use `isort <https://pypi.org/project/isort/>`_ to keep the
imports ordered and consistent with other examples.
.. code-block:: bash
- $ flake8 --ignore=E266 your_file.py
+ $ flake8 --config pyside-setup/.flake8 your_file.py
$ isort your_file.py
* ``json_inheritance.py`` (env var ``INHERITANCE_FILE``) reads a
inheritance.json file containing the class hierarchy generated by
shiboken's doc generator.
+
* ``import_inheritance.py`` actually tries to import the class (legacy)
a feature-aware version of ``.pyi`` files to get a correct display. The simplest
way to change them all in-place is the command:
-.. code-block:: python
+.. code-block:: bash
pyside6-genpyi all --feature snake_case true_property
add_port_example.rst
add_tool.rst
documentation.rst
+ adapt_qt.rst
extras.rst
Implementation details
A useful command to change all .pyi files to use all features is
-.. code-block:: python
+.. code-block:: bash
pyside6-genpyi all --feature snake_case true_property
circumvent this issue.
Applications that don't use Classic Bluetooth will find a subset of
-`QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ is available,
+`QtBluetooth`_ is available,
as CoreBluetooth (Bluetooth LE) do not require either of
:class:`QApplication<PySide6.QtWidgets.QApplication>` or
:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` .
Logging Categories
^^^^^^^^^^^^^^^^^^
-The `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ module
+The `QtBluetooth`_ module
exports the following :class:`logging categories<~.Configuring Categories>` :
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |Logging Category |Description |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth |Enables logging of cross platform code path in `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_|
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth.android|Enables logging of the Android implementation |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth.bluez |Enables logging of the BLuez/Linux implementation |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth.ios |Enables logging of the iOS implementation |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth.osx |Enables logging of the macOS implementation |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
- |qt.bluetooth.windows|Enables logging of the Qt for Windows implementation |
- +--------------------+--------------------------------------------------------------------------------------------------------------+
+ +--------------------+-------------------------------------------------------------+
+ |Logging Category |Description |
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth |Enables logging of cross platform code path in `QtBluetooth`_|
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth.android|Enables logging of the Android implementation |
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth.bluez |Enables logging of the BLuez/Linux implementation |
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth.ios |Enables logging of the iOS implementation |
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth.osx |Enables logging of the macOS implementation |
+ +--------------------+-------------------------------------------------------------+
+ |qt.bluetooth.windows|Enables logging of the Qt for Windows implementation |
+ +--------------------+-------------------------------------------------------------+
Logging categories can be used to enable additional warning and debug output
-for `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ . More
-detailed information about logging can be found in
+for `QtBluetooth`_ . More detailed information about logging can be found in
:class:`QLoggingCategory<~.QLoggingCategory>` . A quick way to enable all
-`QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ logging is to
-add the following line to the ``main()`` function:
+`QtBluetooth`_ logging is to add the following line to the ``main()``
+function::
- ::
+ QLoggingCategory.setFilterRules("qt.bluetooth* = true")
- QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+.. _QtBluetooth: https://doc.qt.io/qt-6/qtbluetooth-module.html
Example
-------
-::
+.. code-block:: python
# Recommended syntax
@ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
Here is an example that illustrates how to use this
function:
-.. code-block::
+.. code-block:: python
:linenos:
from PySide6.QtCore import QObject, Property
The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
-.. code-block::
-
- Property(self, type: type,
- fget: Optional[Callable] = None,
- fset: Optional[Callable] = None,
- freset: Optional[Callable] = None,
- fdel: Optional[Callable] = None,
- doc: str = '',
- notify: Optional[Callable] = None,
- designable: bool = True,
- scriptable: bool = True,
- stored: bool = True, user: bool = False,
- constant: bool = False,
- final: bool = False) -> PySide6.QtCore.Property
+.. code-block:: python
+
+ Property(self, type: type,
+ fget: Optional[Callable] = None,
+ fset: Optional[Callable] = None,
+ freset: Optional[Callable] = None,
+ fdel: Optional[Callable] = None,
+ doc: str = '',
+ notify: Optional[Callable] = None,
+ designable: bool = True,
+ scriptable: bool = True,
+ stored: bool = True, user: bool = False,
+ constant: bool = False,
+ final: bool = False) -> PySide6.QtCore.Property
Normally, only ``type``, ``fget``and ``fset`` are used.
In the above example, the following lines would be equivalent properties:
-.. code-block::
+.. code-block:: python
- pp = QtCore.Property(int, readPP, setPP) # PySide version
- pp = property(readPP, setPP) # Python version
+ pp = QtCore.Property(int, readPP, setPP) # PySide version
+ pp = property(readPP, setPP) # Python version
As you know from the `Python Docs`_, ``Python`` allows to break the property
creation into multiple steps, using the decorator syntax. We can do this in
``PySide`` as well:
-.. code-block::
+.. code-block:: python
:linenos:
from PySide6.QtCore import QObject, Property
QML requires that the property changes are notified. Here is an
example illustrating how to do this:
-.. code-block::
+.. code-block:: python
:linenos:
from PySide6.QtCore import QObject, Signal, Property
.. currentmodule:: PySide6.QtCore
.. _Signal:
-Signal
-******
+Qt Signal
+*********
Synopsis
--------
Functions
^^^^^^^^^
-+---------------------------------------------------------------------------------------------+
-|def :meth:`connect<Signal.connect>` (receiver) |
-+---------------------------------------------------------------------------------------------+
-|def :meth:`disconnect<Signal.disconnect>` (receiver) |
-+---------------------------------------------------------------------------------------------+
-|def :meth:`emit<Signal.disconnect>` (\*args) |
-+---------------------------------------------------------------------------------------------+
++-----------------------------------------------------+
+|def :meth:`connect<Signal.connect>` (receiver) |
++-----------------------------------------------------+
+|def :meth:`disconnect<Signal.disconnect>` (receiver) |
++-----------------------------------------------------+
+|def :meth:`emit<Signal.disconnect>` (\*args) |
++-----------------------------------------------------+
Detailed Description
--------------------
- The :class:`~.Signal` class provides a way to declare and connect Qt
- signals in a pythonic way.
+The :class:`~.Signal` class provides a way to declare and connect Qt
+signals in a pythonic way.
.. class:: PySide6.QtCore.Signal([type1 [, type2...]] [, name="" [, arguments=[]]])
.. currentmodule:: PySide6.QtCore
.. _Slot:
-Slot
-****
+Qt Slots
+********
Detailed Description
--------------------
*****************************
QPyDesignerContainerExtension is the base class for implementing
-`QDesignerContainerExtension <https://doc.qt.io/qt-6/qdesignercontainerextension.html>`_
+`QDesignerContainerExtension class`_
for a Qt Designer custom widget plugin in Python.
It provides the required inheritance from **QObject**.
+
+.. _QDesignerContainerExtension class: https://doc.qt.io/qt-6/qdesignercontainerextension.html
Detailed Description
--------------------
- The :class:`~.QPyDesignerCustomWidgetCollection` implements
- `QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
- and provides static helper functions for registering custom widgets by
- type or by implementing
- `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+The :class:`~.QPyDesignerCustomWidgetCollection` implements
+`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+and provides static helper functions for registering custom widgets by
+type or by implementing
+`QDesignerCustomWidgetInterface`_ .
- The usage is explained in :ref:`designer_custom_widgets`.
+The usage is explained in :ref:`designer_custom_widgets`.
.. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
Registers an instance of a Python-implemented QWidget by type with Qt Designer.
The optional keyword arguments correspond to the getters of
- `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ :
+ `QDesignerCustomWidgetInterface`_ :
:param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
:param str tool_tip: Tool tip to be shown in the widget box.
.. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
Adds a custom widget (implementation of
- `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_)
+ `QDesignerCustomWidgetInterface`_)
with Qt Designer.
:param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
+
+.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
*******************************
QPyDesignerMemberSheetExtension is the base class for implementing
-`QDesignerMemberSheetExtension <https://doc.qt.io/qt-6/qdesignermembersheetextension.html>`_
+`QDesignerMemberSheetExtension class`_
for a Qt Designer custom widget plugin in Python.
It provides the required inheritance from **QObject**.
+
+.. _QDesignerMemberSheetExtension class: https://doc.qt.io/qt-6/qdesignermembersheetextension.html
****************************
QPyDesignerTaskMenuExtension is the base class for implementing
-`QDesignerTaskMenuExtension <https://doc.qt.io/qt-6/qdesignertaskmenuextension.html>`_
+`QDesignerTaskMenuExtension class`_
for a Qt Designer custom widget plugin in Python.
It provides the required inheritance from **QObject**.
+
+.. _QDesignerTaskMenuExtension class: https://doc.qt.io/qt-6/qdesignertaskmenuextension.html
import PySide6.QtMultimedia
-The module also provides `QML types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
+The module also provides QML `types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
Overviews and Important Topics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following table outlines some important QML types.
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |Type |Description |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`MediaPlayer <https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html>`_ |Add audio/video playback functionality to a scene. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`CaptureSession <https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html>`_|Create a session for capturing audio/video. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`Camera <https://doc.qt.io/qt-6/qml-qtmultimedia-camera.html>`_ |Access a camera connected to the system. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`AudioInput <https://doc.qt.io/qt-6/qml-qtmultimedia-audioinput.html>`_ |Access an audio input (microphone) connected to the system. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`AudioOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-audiooutput.html>`_ |Access an audio output (speaker, headphone) connected to the system. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`VideoOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html>`_ |Display video content. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`MediaRecorder <https://doc.qt.io/qt-6/qml-qtmultimedia-mediarecorder.html>`_ |Record audio/video from the `CaptureSession <https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html>`_ . |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`ImageCapture <https://doc.qt.io/qt-6/qml-qtmultimedia-imagecapture.html>`_ |Capture still images from the Camera. |
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- |`Video <https://doc.qt.io/qt-6/qml-qtmultimedia-video.html>`_ |Add Video playback functionality to a scene. Uses `MediaPlayer <https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html>`_ and `VideoOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html>`_ types to provide video playback functionality.|
- +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ +------------------+---------------------------------------------------------------------+
+ |Type |Description |
+ +------------------+---------------------------------------------------------------------+
+ |`MediaPlayer`_ |Add audio/video playback functionality to a scene. |
+ +------------------+---------------------------------------------------------------------+
+ |`CaptureSession`_ |Create a session for capturing audio/video. |
+ +------------------+---------------------------------------------------------------------+
+ |`Camera`_ |Access a camera connected to the system. |
+ +------------------+---------------------------------------------------------------------+
+ |`AudioInput`_ |Access an audio input (microphone) connected to the system. |
+ +------------------+---------------------------------------------------------------------+
+ |`AudioOutput`_ |Access an audio output (speaker, headphone) connected to the system. |
+ +------------------+---------------------------------------------------------------------+
+ |`VideoOutput`_ |Display video content. |
+ +------------------+---------------------------------------------------------------------+
+ |`MediaRecorder`_ |Record audio/video from the `CaptureSession`_ . |
+ +------------------+---------------------------------------------------------------------+
+ |`ImageCapture`_ |Capture still images from the Camera. |
+ +------------------+---------------------------------------------------------------------+
+ |`Video`_ |Add Video playback functionality to a scene. Uses `MediaPlayer`_ and |
+ | |`VideoOutput`_ types to provide video playback functionality. |
+ +------------------+---------------------------------------------------------------------+
+
+.. _MediaPlayer: https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html
+.. _CaptureSession: https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html
+.. _Camera: https://doc.qt.io/qt-6/qml-qtmultimedia-camera.html
+.. _AudioInput: https://doc.qt.io/qt-6/qml-qtmultimedia-audioinput.html
+.. _AudioOutput: https://doc.qt.io/qt-6/qml-qtmultimedia-audiooutput.html
+.. _VideoOutput: https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html
+.. _MediaRecorder: https://doc.qt.io/qt-6/qml-qtmultimedia-mediarecorder.html
+.. _ImageCapture: https://doc.qt.io/qt-6/qml-qtmultimedia-imagecapture.html
+.. _Video: https://doc.qt.io/qt-6/qml-qtmultimedia-video.html
C++ Classes
^^^^^^^^^^^
******************
QPyQmlParserStatus is the base class for implementing
-`QQmlParserStatus <https://doc.qt.io/qt-6/qqmlparserstatus.html>`_
+`QQmlParserStatus class`_ .
It provides the required inheritance from **QObject**.
+
+.. _QQmlParserStatus class: https://doc.qt.io/qt-6/qqmlparserstatus.html
*************************
QPyQmlPropertyValueSource is the base class for implementing
-`QQmlPropertyValueSource <https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html>`_
+`QQmlPropertyValueSource class`_ .
It provides the required inheritance from **QObject**.
+
+.. _QQmlPropertyValueSource class: https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html
.. currentmodule:: PySide6.QtQml
.. _QmlSingleton:
-QmlSingleton
-************
+QmlSingleton decorator
+**********************
.. py:decorator:: QmlSingleton
@QmlElement
@QmlSingleton
class ClassForQml(QObject):
- # ...
+ ...
.. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
@QmlElement
@QmlUncreatable("BaseClassForQml is an abstract base class")
class BaseClassForQml(QObject):
- # ...
+ ...
.. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
.. currentmodule:: PySide6.QtUiTools
-.. _loadUiType:
loadUiType
***********
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
- ...
The `pyside6-uic` tool generates Python code from these `.ui` files,
which you can import from your main files, so it is not necessary
=====================================
When you install ``PySide6`` you might have notice that also ``Shiboken6``
-is installed as a dependency:
+is installed as a dependency::
-.. code-block:: bash
-
- (env) [qt ~]$ pip install pyside6
+ $ pip install pyside6
Collecting pyside6
Downloading PySide6-6.0.0-6.0.0-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl (170.5 MB)
|████████████████████████████████| 170.5 MB 42 kB/s
cmake -B /path/to/the/build/directory \
-S /path/to/the/pyside-setup \
-DCMAKE_INSTALL_PREFIX=/where/to/install \
- -DPYTHON_EXECUTABLE=/path/to/interpreter
+ -DPython_EXECUTABLE=/path/to/interpreter
.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
build which will be compatible with Python 3.7+.
.. code-block:: python
- from PySide2.QtWidgets import QApplication...
+ from PySide2.QtWidgets import QApplication
from PySide2 import QtCore
needs to be changed to:
.. code-block:: python
- from PySide6.QtWidgets import QApplication...
+ from PySide6.QtWidgets import QApplication
from PySide6 import QtCore
"""
+def format_dict(d):
+ """Format the URL dict for error message."""
+ result = '{'
+ n = 0
+ for k, v in d.items():
+ n += 1
+ if n > 10:
+ result += "..."
+ break
+ if n > 1:
+ result += ", "
+ result += f'"{k}": "{v}"'
+ result += '}'
+ return result
+
+
class InheritanceGraph(object):
"""
Given a list of classes, determines the set of classes that they inherit
if url is not None:
this_node_attrs['URL'] = f'"{url}"'
this_node_attrs['target'] = '"_top"' # Browser target frame attribute (same page)
+ else:
+ urls_str = format_dict(urls)
+ print(f'inheritance_graph.py: No URL found for {name} ({fullname}) in {urls_str}.',
+ file=sys.stderr)
attribute = self._format_node_attrs(this_node_attrs)
res.append(f' "{name}" [{attribute}];\n')
.. grid-item-card:: :mod:`Qt3D Render <PySide6.Qt3DRender>`
Contains functionality to support 2D and 3D rendering using Qt 3D.
+
+ .. grid-item-card:: :mod:`QtAsyncio <PySide6.QtAsyncio>`
+
+ Provides integration between asyncio and Qt's event loop.
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import argparse
+import subprocess
+import os
+import sys
+import time
+from multiprocessing import Pool, cpu_count
+from pathlib import Path
+
+
+def run_qdoc(file, qdoc_args, args):
+ env = os.environ
+ env["BUILDDIR"] = args.build_dir
+ env["QT_INSTALL_DOCS"] = args.qt_install_docs
+ env["QT_VERSION"] = args.qt_version
+ env["QT_VER"] = ".".join(args.qt_version.split(".")[:2])
+ env["QT_VERSION_TAG"] = args.qt_version
+
+ command = [
+ args.qdoc_bin,
+ file,
+ *qdoc_args,
+ "-installdir",
+ args.doc_data_dir,
+ "-outputdir",
+ args.doc_data_dir,
+ ]
+
+ start_time = time.time()
+ _ = subprocess.Popen(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = _.communicate()
+ returncode = _.wait()
+ duration = round(time.time() - start_time)
+
+ if args.verbose or returncode != 0 or err:
+ cmd_str = " ".join(command)
+ print(f"> Output of: {cmd_str}")
+ if out:
+ print(out.decode("utf-8"), file=sys.stdout)
+ if err:
+ print(err.decode("utf-8"), file=sys.stderr)
+ print(f"> Finished: {file} {duration}s (exit code {returncode})")
+
+ if returncode != 0:
+ raise Exception(f"Failing executing the command {command} ({returncode})")
+
+
+def get_qdocconf_files():
+ if not Path("pyside.qdocconf").exists():
+ print("ERROR: the working dir doesn't include a 'pyside.qdocconf' file")
+ sys.exit(-1)
+
+ # Generate the temporary qdocconf files
+ # This is necessary because using a file like 'pyside-qtcore.qtdocconf'
+ # will generate an error, because inside we call functions like 'include()'
+ files_single_exec = []
+ files_prepare = []
+ with open("pyside.qdocconf") as f:
+ for i in f.read().splitlines():
+ _p = Path(i)
+ _name = f"_{_p.stem}.qdocconf"
+ with open(_name, "w", encoding="utf-8") as f:
+ f.write(i)
+ files_single_exec.append(_name)
+ files_prepare.append(i.strip())
+
+ return files_prepare, files_single_exec
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(prog="qdoc spawner")
+ parser.add_argument("--qt", dest="qt_version", action="store", required=True)
+ parser.add_argument("--doc-data-dir", dest="doc_data_dir", action="store", required=True)
+ parser.add_argument("--qdoc-binary", dest="qdoc_bin", action="store", required=True)
+ parser.add_argument("--build-dir", dest="build_dir", action="store", required=True)
+ parser.add_argument("--qt-install-docs", dest="qt_install_docs", action="store", required=True)
+ parser.add_argument("--parallel", dest="parallel", action="store", default="4")
+ parser.add_argument("--verbose", dest="verbose", action="store_true", default=False)
+
+ args = parser.parse_args()
+ files_prepare, files_single_exec = get_qdocconf_files()
+
+ parallel = args.parallel
+ if parallel == "auto":
+ parallel = cpu_count()
+
+ try:
+ # mode: -prepare -no-link-errors
+ with Pool(int(parallel)) as p:
+ p.starmap(run_qdoc, [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare])
+
+ # mode: -single-exec
+ with Pool(int(parallel)) as p:
+ p.starmap(run_qdoc, [(str(f), ["-single-exec"], args) for f in files_single_exec])
+ except Exception as e:
+ print(f"qdoc_spawner: error: {e}", file=sys.stderr)
+ sys.exit(-1)
Installation
------------
+.. note:: For a commercial installation, refer to :ref:`commercial-page`.
+
* **Creating and activating an environment**
You can do this by running the following on a terminal:
button.clicked.connect(say_hello)
Finally, we show the button and start the Qt main loop:
-::
+
+.. code-block:: python
# Show the button
button.show()
app.exec()
Here is the complete code for this example:
-::
- #!/usr/bin/python
+.. code-block:: python
import sys
from PySide6.QtWidgets import QApplication, QPushButton
-------------
Here is the complete code for this tutorial:
-::
+
+.. code-block:: python
import sys
from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication,
Qt Linguist
-----------
-`Qt Linguist <https://doc.qt.io/qt-6/qtlinguist-index.html>`_ and
+`Qt Linguist`_ and
its related tools can be used to provide translations for applications.
-The ``examples/widgets/linguist`` example illustrates this. The example is
+The :ref:`qt-linguist-example` example illustrates this. The example is
very simple, it has a menu and shows a list of programming languages with
multiselection.
LANG=de python main.py
+.. _Qt Linguist: https://doc.qt.io/qt-6/qtlinguist-index.html
+
GNU gettext
-----------
-The `GNU gettext <https://docs.python.org/3/library/gettext.html>`_ module
+The `GNU gettext`_ module
can be used to provide translations for applications.
-The ``examples/widgets/gettext`` example illustrates this. The example is
+The :ref:`gettext-example` example illustrates this. The example is
very simple, it has a menu and shows a list of programming languages with
multiselection.
.. code-block:: python
import gettext
- ...
+ # ...
_ = None
ngettext = None
.. code-block:: bash
LANG=de python main.py
+
+.. _GNU gettext: https://docs.python.org/3/library/gettext.html
further on. To know more about the Model/View architecture in Qt, refer to
its `official documentation <https://doc.qt.io/qt-6/model-view-programming.html>`_.
-1. Import ``QTreeWidget`` and ``QTreeWidgetItem`` for this application:
+#. Import ``QTreeWidget`` and ``QTreeWidgetItem`` for this application:
.. code-block:: python
import sys
from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
-2. Define a dictionary with project structures to display the information as a
+#. Define a dictionary with project structures to display the information as a
tree, with files belonging to each project:
.. code-block:: python
"Project B": ["file_b.csv", "photo.jpg"],
"Project C": []}
-3. Initialize the ``QApplication`` singleton:
+#. Initialize the ``QApplication`` singleton:
.. code-block:: python
app = QApplication()
-4. Configure the ``QTreeWidget`` to have two columns, one for the item name,
+#. Configure the ``QTreeWidget`` to have two columns, one for the item name,
and the other for item type information of the files in the project
directories.
You can set the column name with the ``setHeaderLabels`` as described below:
tree.setColumnCount(2)
tree.setHeaderLabels(["Name", "Type"])
-5. Iterate the data structure, create the ``QTreeWidgetItem`` elements, and add
+#. Iterate the data structure, create the ``QTreeWidgetItem`` elements, and add
the corresponding children to each parent.
We also extract the extension name for only the files and add them
into the second column.
tree.insertTopLevelItems(0, items)
-7. Show the tree and execute the ``QApplication``.
+#. Show the tree and execute the ``QApplication``.
.. code-block:: python
Then to execute it we just need to run the following on a
command prompt:
-.. code-block:: python
+.. code-block:: bash
python main.py
`Using Custom Widgets with Qt Designer <https://doc.qt.io/qt-6/designer-using-custom-widgets.html>`_
). Normally, this requires implementing the widget as a plugin to Qt Designer
written in C++ implementing its
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+`QDesignerCustomWidgetInterface`_ .
Qt for Python provides a simple interface for this which is similar to
:meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`.
QPyDesignerCustomWidgetCollection provides an implementation of
-`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+`QDesignerCustomWidgetCollectionInterface`_
exposing custom widgets to **Qt Designer** with static convenience functions
for registering types or adding instances of
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+`QDesignerCustomWidgetInterface`_ .
The function
:meth:`registerCustomWidget()<PySide6.QtDesigner.QPyDesignerCustomWidgetCollection.registerCustomWidget>`
can be used like ``QUiLoader.registerCustomWidget()``. It takes the custom widget
type and some optional keyword arguments passing values that correspond to the
getters of
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ :
+`QDesignerCustomWidgetInterface`_ :
When launching **Qt Designer** via its launcher ``pyside6-designer``,
the custom widget should be visible in the widget box.
corresponding C++
`Task Menu Extension Example <https://doc.qt.io/qt-6/qtdesigner-taskmenuextension-example.html>`_ .
+.. _QDesignerCustomWidgetCollectionInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html
+.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
+
Troubleshooting the Qt Designer Plugin
++++++++++++++++++++++++++++++++++++++
.. code-block:: python
:linenos:
- if __name__ == "__main__":
- app = QApplication([])
- # ...
- sys.exit(app.exec())
+ if __name__ == "__main__":
+ app = QApplication([])
+ # ...
+ sys.exit(app.exec())
Now, to start the development, create an empty window called `MainWindow`.
You could do that by defining a class that inherits from `QMainWindow`.
.. grid-item-card:: Basic Connections
:class-item: cover-img
- :link: basictutorial/signal_and_slots.html
+ :link: basictutorial/signals_and_slots.html
:img-top: basictutorial/signals_slots.png
Signals and Slots
the C++ and Python versions of these functions look like:
C++ version
-------------
+-----------
.. literalinclude:: initdb.h
:language: c++
:lines: 9-33
Python version
----------------
+--------------
.. literalinclude:: createdb.py
:language: python
Here is how the C++ and Python versions of this function
looks like:
-C++ version
-------------
+C++ version (initDb)
+--------------------
.. literalinclude:: initdb.h
:language: c++
:linenos:
:lines: 35-112
-Python version
----------------
+Python version (init_db)
+------------------------
.. literalinclude:: createdb.py
:language: python
Use the following command from the prompt to run:
-.. code-block::
+.. code-block:: bash
python main.py
each book in the table. Here is how the reimplemented
code looks like:
-C++ version
-------------
+C++ version (bookdelegate)
+--------------------------
.. literalinclude:: bookdelegate.cpp
:language: c++
:linenos:
:lines: 22-
-Python version
----------------
+Python version (bookdelegate)
+-----------------------------
.. literalinclude:: bookdelegate.py
:language: python
To generate this Python code, run the following command on the
prompt:
-.. code-block::
+.. code-block:: bash
- pyside6-uic bookwindow.ui -o ui_bookwindow.py
+ pyside6-uic bookwindow.ui -o ui_bookwindow.py
Try porting the remaining code now. To begin with, here is
how both the versions of the constructor code looks:
Now, run the ``pyside6-rcc`` tool on the ``books.qrc`` file
to generate ``rc_books.py``.
-.. code-block::
+.. code-block:: bash
- pyside6-rcc books.qrc -o rc_books.py
+ pyside6-rcc books.qrc -o rc_books.py
Once you have the Python script generated, make the
following changes to ``bookdelegate.py`` and ``main.py``:
.. _attached properties: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html
.. _Layout.fillWidth: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillWidth-attached-prop
.. _Layout.fillHeight: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillHeight-attached-prop
-.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html
.. _Qt Quick QML Types: https://doc.qt.io/qt-5/qtquick-qmlmodule.html
Let's look at the ``Listview`` in detail:
.. grid:: 1 3 3 3
:gutter: 2
+ .. grid-item-card:: PyConES 2023
+ :img-top: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
+ :link: https://www.youtube.com/embed/XuqdTvisqkQ
+
+ Asynchronous programming with asyncio and Qt
+
.. grid-item-card:: QtWS 2022
:img-top: https://img.youtube.com/vi/8wcdN1Iw1Uk/mqdefault.jpg
:link: https://www.youtube.com/embed/8wcdN1Iw1Uk
set(libpyside_libraries Qt::Core Qt::CorePrivate)
+set(CMAKE_AUTOMOC ON)
+
set(libpyside_HEADERS # installed below
+ pysideqslotobject_p.h
class_property.h
dynamicqmetaobject.h
feature_select.h
)
set(libpyside_SRC
+ pysideqslotobject_p.cpp
class_property.cpp
dynamicqmetaobject.cpp
feature_select.cpp
#include <autodecref.h>
#include <gilstate.h>
-#include <QtCore/qhashfunctions.h>
#include <QtCore/QMetaMethod>
#include <QtCore/QSet>
#include <QtCore/QDebug>
namespace PySide
{
-size_t qHash(const GlobalReceiverKey &k, size_t seed)
-{
- QtPrivate::QHashCombine hash;
- seed = hash(seed, k.object);
- seed = hash(seed, k.method);
- return seed;
-}
-
class DynamicSlotDataV2
{
Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
#include "dynamicqmetaobject.h"
#include <QtCore/QByteArray>
+#include <QtCore/QHashFunctions>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QMap>
{
const PyObject *object;
const PyObject *method;
+
+ friend constexpr size_t qHash(GlobalReceiverKey k, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, k.object, k.method);
+ }
};
inline bool operator==(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
return k1.object != k2.object || k1.method != k2.method;
}
-size_t qHash(const GlobalReceiverKey &k, size_t seed = 0);
-
/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
/// providing fake slots for QObject::connect().
/// It keeps a Python callback and the list of QObject senders. It is stored
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysideqslotobject_p.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+
+namespace PySide
+{
+
+void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver,
+ void **args, bool *ret)
+{
+ auto self = static_cast<PySideQSlotObject *>(this_);
+ switch (which) {
+ case Destroy:
+ delete self;
+ break;
+ case Call:
+ {
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(0));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist));
+ break;
+ }
+ case Compare:
+ case NumOperations:
+ Q_UNUSED(receiver);
+ Q_UNUSED(args);
+ Q_UNUSED(ret);
+ break;
+ }
+}
+
+} // namespace PySide
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PYSIDEQSLOTOBJECT_P_H
+#define PYSIDEQSLOTOBJECT_P_H
+
+#include "pysidemacros.h"
+#include <sbkpython.h>
+
+#include <QtCore/QObject>
+#include <QtCore/qobjectdefs.h>
+
+namespace PySide
+{
+
+class PySideQSlotObject : public QtPrivate::QSlotObjectBase
+{
+ PyObject *callable;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret);
+
+public:
+ PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable)
+ {
+ Py_INCREF(callable);
+ }
+
+ ~PySideQSlotObject()
+ {
+ auto gstate = PyGILState_Ensure();
+ Py_DECREF(callable);
+ PyGILState_Release(gstate);
+ }
+};
+
+
+} // namespace PySide
+
+#endif // PYSIDEQSLOTOBJECT_P_H
}
namespace PySide::Signal {
- //aux
- class SignalSignature {
- public:
- SignalSignature() = default;
- explicit SignalSignature(QByteArray parameterTypes) :
- m_parameterTypes(std::move(parameterTypes)) {}
- explicit SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) :
- m_parameterTypes(std::move(parameterTypes)),
- m_attributes(attributes) {}
-
- QByteArray m_parameterTypes;
- QMetaMethod::Attributes m_attributes = QMetaMethod::Compatibility;
- };
-
static QByteArray buildSignature(const QByteArray &, const QByteArray &);
- static void appendSignature(PySideSignal *, const SignalSignature &);
static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
- static QByteArray parseSignature(PyObject *);
+ static PySideSignalData::Signature parseSignature(PyObject *);
static PyObject *buildQtCompatible(const QByteArray &);
} // PySide::Signal
PyObject *arg = PyTuple_GET_ITEM(args, i);
if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) {
tupledArgs = true;
- const auto sig = PySide::Signal::parseSignature(arg);
- PySide::Signal::appendSignature(
- self,
- PySide::Signal::SignalSignature(sig));
+ self->data->signatures.append(PySide::Signal::parseSignature(arg));
}
}
- if (!tupledArgs) {
- const auto sig = PySide::Signal::parseSignature(args);
- PySide::Signal::appendSignature(
- self,
- PySide::Signal::SignalSignature(sig));
- }
+ if (!tupledArgs)
+ self->data->signatures.append(PySide::Signal::parseSignature(args));
return 0;
}
auto self = reinterpret_cast<PySideSignal *>(obSelf);
QByteArray sigKey;
if (key) {
- sigKey = PySide::Signal::parseSignature(key);
+ sigKey = PySide::Signal::parseSignature(key).signature;
} else {
sigKey = self->data == nullptr || self->data->signatures.isEmpty()
? PySide::Signal::voidType() : self->data->signatures.constFirst().signature;
{
auto *firstSignal = reinterpret_cast<PySideSignalInstance *>(self);
const auto &sigName = firstSignal->d->signalName;
- const auto sigKey = PySide::Signal::parseSignature(key);
+ const auto sigKey = PySide::Signal::parseSignature(key).signature;
const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
if (data->d->signature == sig) {
return QMetaObject::normalizedSignature(name + '(' + signature + ')');
}
-static QByteArray parseSignature(PyObject *args)
+static PySideSignalData::Signature parseSignature(PyObject *args)
{
- if (args && (Shiboken::String::check(args) || !PyTuple_Check(args)))
- return getTypeName(args);
+ PySideSignalData::Signature result{{}, QMetaMethod::Compatibility};
+ if (args && (Shiboken::String::check(args) || !PyTuple_Check(args))) {
+ result.signature = getTypeName(args);
+ return result;
+ }
- QByteArray signature;
for (Py_ssize_t i = 0, i_max = PySequence_Size(args); i < i_max; i++) {
Shiboken::AutoDecRef arg(PySequence_GetItem(args, i));
const auto typeName = getTypeName(arg);
if (!typeName.isEmpty()) {
- if (!signature.isEmpty())
- signature += ',';
- signature += typeName;
+ if (!result.signature.isEmpty())
+ result.signature += ',';
+ result.signature += typeName;
}
}
- return signature;
-}
-
-static void appendSignature(PySideSignal *self, const SignalSignature &signature)
-{
- self->data->signatures.append({signature.m_parameterTypes, signature.m_attributes});
+ return result;
}
static void sourceGone(void *data)
return root;
}
-template<typename T>
-static typename T::value_type join(T t, const char *sep)
-{
- typename T::value_type res;
- if (t.isEmpty())
- return res;
-
- typename T::const_iterator it = t.begin();
- typename T::const_iterator end = t.end();
- res += *it;
- ++it;
-
- while (it != end) {
- res += sep;
- res += *it;
- ++it;
- }
- return res;
-}
-
static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
{
Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
}
// This function is used by qStableSort to promote empty signatures
-static bool compareSignals(const SignalSignature &sig1, const SignalSignature &)
+static bool compareSignals(const PySideSignalData::Signature &sig1,
+ const PySideSignalData::Signature &sig2)
{
- return sig1.m_parameterTypes.isEmpty();
+ return sig1.signature.isEmpty() && !sig2.signature.isEmpty();
}
static PyObject *buildQtCompatible(const QByteArray &signature)
void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
{
- using SignalSigMap = QHash<QByteArray, QList<SignalSignature> >;
+ using Signature = PySideSignalData::Signature;
+ using SignalSigMap = QHash<QByteArray, QList<Signature>>;
SignalSigMap signalsFound;
for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) {
QMetaMethod method = metaObject->method(i);
if (method.methodType() == QMetaMethod::Signal) {
QByteArray methodName(method.methodSignature());
methodName.chop(methodName.size() - methodName.indexOf('('));
- SignalSignature signature;
- signature.m_parameterTypes = join(method.parameterTypes(), ",");
+ Signature signature{method.parameterTypes().join(','), {}};
if (method.attributes() & QMetaMethod::Cloned)
- signature.m_attributes = QMetaMethod::Cloned;
+ signature.attributes = QMetaMethod::Cloned;
signalsFound[methodName] << signature;
}
}
self->homonymousMethod = nullptr;
// Empty signatures comes first! So they will be the default signal signature
- std::stable_sort(it.value().begin(), it.value().end(), &compareSignals);
- const auto endJ = it.value().cend();
- for (auto j = it.value().cbegin(); j != endJ; ++j) {
- const SignalSignature &sig = *j;
- appendSignature(self, sig);
- }
+ self->data->signatures = it.value();
+ std::stable_sort(self->data->signatures.begin(),
+ self->data->signatures.end(), &compareSignals);
_addSignalToWrapper(pyObj, it.key(), self);
Py_DECREF(reinterpret_cast<PyObject *>(self));
{
struct Signature
{
- QByteArray signature;
- int attributes;
+ QByteArray signature; // ','-separated list of parameter types
+ unsigned short attributes;
};
QByteArray signalName;
{
QByteArray signalName;
QByteArray signature;
- int attributes = 0;
PyObject *source = nullptr;
PyObject *homonymousMethod = nullptr;
PySideSignalInstance *next = nullptr;
+ unsigned short attributes = 0;
};
namespace PySide::Signal {
#include "qobjectconnect.h"
#include "pysideqobject.h"
+#include "pysideqslotobject_p.h"
#include "pysidesignal.h"
#include "pysideutils.h"
#include "signalmanager.h"
#include <QtCore/QMetaMethod>
#include <QtCore/QObject>
+#include <QtCore/private/qobject_p.h>
+
#include <string_view>
static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self)
return connection;
}
+QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+ PyObject *callback, Qt::ConnectionType type)
+{
+ if (!signal || !PySide::Signal::checkQtSignal(signal))
+ return {};
+
+ const int signalIndex =
+ PySide::SignalManager::registerMetaMethodGetIndex(source, signal + 1,
+ QMetaMethod::Signal);
+ if (signalIndex == -1)
+ return {};
+
+ // Extract receiver from callback
+ const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
+ if (receiver.receiver == nullptr && receiver.self == nullptr)
+ return {};
+
+ PySide::SignalManager &signalManager = PySide::SignalManager::instance();
+
+ PySideQSlotObject *slotObject = new PySideQSlotObject(callback);
+
+ QMetaObject::Connection connection{};
+ Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
+ connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type);
+ Py_END_ALLOW_THREADS
+ if (!connection) {
+ if (receiver.usingGlobalReceiver)
+ signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ return {};
+ }
+
+ Q_ASSERT(receiver.receiver);
+ if (receiver.usingGlobalReceiver)
+ signalManager.notifyGlobalReceiver(receiver.receiver);
+
+ const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
+ static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
+ return connection;
+}
+
bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback)
{
if (!PySide::Signal::checkQtSignal(signal))
qobjectConnectCallback(QObject *source, const char *signal,
PyObject *callback, Qt::ConnectionType type);
+/// Helpers for QObject::connect(): Make a connection to a Python callback and a context object
+PYSIDE_API QMetaObject::Connection
+ qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+ PyObject *callback, Qt::ConnectionType type);
+
/// Helpers for QObject::disconnect(): Disconnect a Python callback
PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal,
PyObject *callback);
#include "pysideqmlmacros.h"
#include <sbkpython.h>
+#include <QtCore/qtconfigmacros.h>
+QT_BEGIN_NAMESPACE
namespace QQmlPrivate
{
struct RegisterType;
}
+QT_END_NAMESPACE
namespace PySide::Qml
{
// Used by QtQuick module to fill the QQmlPrivate::RegisterType::parserStatusCast,
// valueSourceCast and valueInterceptorCast fields with the correct values.
using QuickRegisterItemFunction =
- bool (*)(PyObject *pyObj, QQmlPrivate::RegisterType *);
+ bool (*)(PyObject *pyObj, QT_PREPEND_NAMESPACE(QQmlPrivate::RegisterType) *);
PYSIDEQML_API QuickRegisterItemFunction getQuickRegisterItemFunction();
PYSIDEQML_API void setQuickRegisterItemFunction(QuickRegisterItemFunction function);
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef slots
#include <Python.h> // Include before Qt headers due to 'slots' macro definition
#include "designercustomwidgets.h"
add_subdirectory(tools/metaobjectdump)
add_subdirectory(tools/pyside6-deploy)
+if(UNIX AND NOT APPLE)
+ add_subdirectory(tools/pyside6-android-deploy)
+endif()
+
if (NOT DISABLE_QtQuick)
add_subdirectory(tools/pyside6-qml)
endif()
class QAsyncioTestCase(unittest.TestCase):
async def sleep(self, output):
output += "Hello"
- await asyncio.sleep(1)
+ await asyncio.sleep(0.2)
output += "World"
async def gather(self, output):
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for QtAsyncio'''
+
+import asyncio
+import unittest
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseCancelTask(unittest.TestCase):
+ # Taken from https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancel
+
+ async def cancel_me(self, output):
+ output += "(1) cancel_me(): before sleep"
+
+ try:
+ await asyncio.sleep(10)
+ except asyncio.CancelledError:
+ output += "(2) cancel_me(): cancel sleep"
+ raise
+ finally:
+ output += "(3) cancel_me(): after sleep"
+
+ async def main(self, output):
+ task = asyncio.create_task(self.cancel_me(output))
+ await asyncio.sleep(0.1)
+ task.cancel()
+ try:
+ await task
+ except asyncio.CancelledError:
+ output += "(4) main(): cancel_me is cancelled now"
+
+ def test_await_tasks(self):
+ output_expected = []
+ output_real = []
+
+ asyncio.run(self.main(output_expected))
+ QtAsyncio.run(self.main(output_real), keep_running=False)
+
+ self.assertEqual(output_real, output_expected)
+
+
+if __name__ == '__main__':
+ unittest.main()
return result
async def chain(self, output, n):
- link1 = await self.link(output, n, 1)
- link2 = await self.link(output, n, 2)
+ link1 = await self.link(output, n, 0.2)
+ link2 = await self.link(output, n, 0.5)
output += f"chain {n}: {link1} -> {link2} "
async def gather(self, output, *args):
async def run_asyncio_executor(self):
main_thread = QThread.currentThread()
with ThreadPoolExecutor(max_workers=2) as executor:
- result = await asyncio.get_running_loop().run_in_executor(executor, self.blocking_function)
+ result = await asyncio.get_running_loop().run_in_executor(
+ executor, self.blocking_function)
# Assert that we are back to the main thread.
self.assertEqual(QThread.currentThread(), main_thread)
class QAsyncioTestCaseQueues(unittest.TestCase):
async def produce(self, output, queue):
- for _ in range(random.randint(0, 3)):
- await asyncio.sleep(random.randint(0, 2))
+ for _ in range(random.randint(0, 2)):
+ await asyncio.sleep(random.random())
await queue.put(self.i)
output += f"{self.i} added to queue\n"
self.i += 1
async def consume(self, output, queue):
while True:
- await asyncio.sleep(random.randint(0, 2))
+ await asyncio.sleep(random.random())
i = await queue.get()
output += f"{i} pulled from queue\n"
queue.task_done()
async def main(self, output1, output2, num_producers, num_consumers):
self.i = 0
- queue = asyncio.Queue() # type: asyncio.Queue
- producers = [asyncio.create_task(self.produce(output1, queue)) for _ in range(num_producers)]
- consumers = [asyncio.create_task(self.consume(output2, queue)) for _ in range(num_consumers)]
+ queue = asyncio.Queue()
+ producers = [
+ asyncio.create_task(self.produce(output1, queue)) for _ in range(num_producers)]
+ consumers = [
+ asyncio.create_task(self.consume(output2, queue)) for _ in range(num_consumers)]
await asyncio.gather(*producers)
await queue.join()
for consumer in consumers:
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for QtAsyncio'''
+
+import unittest
+import asyncio
+import threading
+import time
+
+from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+
+
+class QAsyncioTestCaseThreadsafe(unittest.TestCase):
+
+ def setUp(self) -> None:
+ super().setUp()
+ asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
+ self.loop_event = asyncio.Event()
+
+ def thread_target(self, is_threadsafe):
+ time.sleep(1)
+ if is_threadsafe:
+ # call_soon_threadsafe() wakes the loop that is in another thread, so the
+ # loop checks the event and will not hang.
+ asyncio.get_event_loop().call_soon_threadsafe(self.loop_event.set)
+ else:
+ # call_soon() does not wake the loop that is in another thread, and so the
+ # loop keeps waiting without checking the event and will hang.
+ asyncio.get_event_loop().call_soon(self.loop_event.set)
+
+ async def coro(self, is_threadsafe):
+ thread = threading.Thread(target=self.thread_target, args=(is_threadsafe,))
+ thread.start()
+
+ task = asyncio.create_task(self.loop_event.wait())
+
+ # The timeout is necessary because the loop will hang for the non-threadsafe case.
+ done, pending = await asyncio.wait([task], timeout=2)
+
+ thread.join()
+
+ if is_threadsafe:
+ self.assertEqual(len(done), 1)
+ self.assertEqual(len(pending), 0)
+ else:
+ self.assertEqual(len(done), 0)
+ self.assertEqual(len(pending), 1)
+
+ def test_not_threadsafe(self):
+ asyncio.run(self.coro(False))
+
+ def test_threadsafe(self):
+ asyncio.run(self.coro(True))
+
+
+if __name__ == '__main__':
+ unittest.main()
asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
loop = asyncio.new_event_loop()
- end_time = loop.time() + 5.0
+ end_time = loop.time() + 3.0
loop.call_soon(self.display_date, end_time, loop)
try:
from PySide6.QtBluetooth import QBluetoothLocalDevice
+
class QBluetoothLocalDeviceTest(unittest.TestCase):
def testInitialization(self):
device = QBluetoothLocalDevice()
+
if __name__ == '__main__':
unittest.main()
def _checkCleanup():
global callCleanup
- assert(callCleanup)
+ assert (callCleanup)
app = QCoreApplication([])
self.assertEqual(ci.value(), 'http://www.pyside.org')
def test_dictionary(self):
- @ClassInfo({'author':'pyside', 'author company':'The Qt Company'})
+ @ClassInfo({'author': 'pyside', 'author company': 'The Qt Company'})
class MyObject(QObject):
pass
self.assertEqual(ci.value(), 'The Qt Company')
def test_verify_metadata_types(self):
- valid_dict = { '123': '456' }
+ valid_dict = {'123': '456'}
- invalid_dict_1 = { '123': 456 }
- invalid_dict_2 = { 123: 456 }
- invalid_dict_3 = { 123: '456' }
+ invalid_dict_1 = {'123': 456}
+ invalid_dict_2 = {123: 456}
+ invalid_dict_3 = {123: '456'}
ClassInfo(**valid_dict)
def test_can_only_be_used_on_qobjects(self):
def make_info():
return ClassInfo(author='pyside')
+
def test_function():
pass
self.assertRaises(TypeError, make_info(), test_function)
self.verticalLayout.addWidget(self.pushButton)
-
self.horizontalLayout.addLayout(self.verticalLayout)
MainWindow.setCentralWidget(self.centralwidget)
param = None
+
def handler(msgt, ctx, msg):
global param
param = ctx.category + ": " + msg.strip()
qCWarning(self.criticalCategory, f"devices: {self.no_devices}")
self.assertEqual(param, "warning.log: devices: 2")
-
def test_qCritical(self):
qCCritical(self.defaultCategory, "no device")
self.assertEqual(param, "default: no device")
MethodDescriptorType = type(str.split)
+
def xprint(*args, **kw):
if "-v" in sys.argv:
print(*args, **kw)
init_test_paths(False)
from PySide6.QtCore import (QByteArray, QCborStreamReader, QCborStreamWriter,
- QCborTag, QCborValue)
+ QCborTag, QCborValue)
class TestCbor(unittest.TestCase):
for i, child in enumerate(children):
self.assertEqual(child, parent.findChild(QObject, f'object{i}'))
-
def testFindChildOptions(self):
parent = QObject()
- child = QObject(parent)
+ child = QObject(parent)
nested_child_name = 'nestedChild'
nested_child = QObject(child)
nested_child.setObjectName(nested_child_name)
if carriage_return != -1:
orig.remove(carriage_return, 1)
- f = QFile(':/quote.txt') #|QIODevice.Text
+ f = QFile(':/quote.txt') # |QIODevice.Text
self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
copy = f.readAll()
f.close()
self.assertTrue(thread.called)
self.assertEqual(self.qthread, thread.qthread)
+ def testSingleShotWithContextZero(self):
+ thread = ThreadForContext()
+ thread.start()
+ thread.context.moveToThread(thread)
+ QTimer.singleShot(0, thread.context, self.callback)
+ self.app.exec()
+ thread.wait()
+ self.assertTrue(self.called)
+ self.assertTrue(thread.called)
+ self.assertEqual(self.qthread, thread.qthread)
+
class SigEmitter(QObject):
window.modal
from __feature__ import snake_case, true_property
- #PYSIDE-1548: Make sure that another import does not clear the features.
+ # PYSIDE-1548: Make sure that another import does not clear the features.
import sys
self.assertTrue(isinstance(QWidget.modal, property))
self.assertTrue(data_proxy.rowCount(), 4)
def testDefaultSurfaceFormat(self):
- format = qDefaultSurfaceFormat(True)
- print(format)
+ format = qDefaultSurfaceFormat(True)
+ print(format)
def testQValue3DAxisFormatter(self):
"""PYSIDE-2025: Test the added setters of QValue3DAxisFormatter."""
def testGetCmykF(self): # not supported by colorsys
for x, y in zip(self.color.getCmykF(), (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
- self.assertTrue(x - y < 1/10000.0)
+ self.assertTrue(x - y < 1 / 10000.0)
class QColorQRgbConstructor(unittest.TestCase):
'''QFontMetrics.boundingRect(QRect, ...) - type error'''
arg = QRect(0, 0, 100, 200)
self.assertRaises(TypeError, self.metrics.boundingRect, arg,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextExpandTabs | Qt.AlignLeft,
+ 'PySide by INdT', 20, ['aaaa', 'ase'])
class SizeTest(QFontMetricsTest):
def testTypeError(self):
'''QFontMetricsF.size - type error'''
self.assertRaises(TypeError, self.metrics.size,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextExpandTabs | Qt.AlignLeft,
+ 'PySide by INdT', 20, ['aaaa', 'ase'])
class QCharTest(QFontMetricsFTest):
PIX_PATH = os.fspath(Path(__file__).resolve().parents[2]
/ "doc/tutorials/basictutorial/icons.png")
+
class QIconAddPixmapTest(TimedQGuiApplication):
'''PYSIDE-1669: check that addPixmap works'''
from PySide6.QtCore import QSize, QTimer, Qt
from PySide6.QtGui import (QColor, QGuiApplication, QImage, QOpenGLContext,
- QSurfaceFormat)
+ QSurfaceFormat)
from PySide6.QtOpenGL import QOpenGLTexture, QOpenGLWindow
QML_IMPORT_NAME = "test.PythonObject"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class PythonObject(QObject):
def __init__(self):
QML_IMPORT_NAME = "test.RotateValue"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class RotateValue(QObject):
def __init__(self):
# This should segfault if the QDeclarativeComponent has not QQmlEngine
file = Path(__file__).resolve().parent / 'foo.qml'
-assert(not file.is_file())
+assert (not file.is_file())
component.loadUrl(QUrl.fromLocalFile(file))
QML_IMPORT_NAME = "test.ProxyObject"
QML_IMPORT_MAJOR_VERSION = 1
+
+
@QmlElement
class ProxyObject(QObject):
def __init__(self):
QML_IMPORT_NAME = "test.ListModel"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class ListModel(QAbstractListModel):
def __init__(self):
super().__init__()
def roleNames(self):
- return { Qt.DisplayRole: b'pysideModelData' }
+ return {Qt.DisplayRole: b'pysideModelData'}
def rowCount(self, parent=QModelIndex()):
return 3
class A(object, metaclass=MetaA):
pass
+
MetaB = type(QQuickPaintedItem)
B = QQuickPaintedItem
class TestBug926 (unittest.TestCase):
def testIt(self):
app = QGuiApplication([])
- qmlRegisterType(MyClass,'Example', 1, 0, 'MyClass')
+ qmlRegisterType(MyClass, 'Example', 1, 0, 'MyClass')
view = QQuickView()
file = Path(__file__).resolve().parent / 'bug_926.qml'
self.assertTrue(file.is_file())
app = QGuiApplication([])
file = Path(__file__).resolve().parent / 'bug_995.qml'
-assert(file.is_file())
+assert (file.is_file())
view = QQuickView(QUrl.fromLocalFile(file))
view.show()
view.resize(200, 200)
root = view.rootObject()
self.assertTrue(root, quickview_errorstring(view))
button = root.findChild(QObject, "buttonMouseArea")
- self.assertRaises(TypeError, QObject.connect, [button,SIGNAL('entered()'), self.onButtonFailClicked])
+ self.assertRaises(TypeError, QObject.connect,
+ [button, SIGNAL('entered()'), self.onButtonFailClicked])
button.entered.connect(self.onButtonClicked)
button.entered.emit()
view.show()
class MyObject(QObject):
titleChanged = Signal()
+
def __init__(self, text, parent=None):
QObject.__init__(self, parent)
self._text = text
QML_IMPORT_NAME = "Singletons"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
@QmlSingleton
class DecoratedSingletonQObject(QObject):
QML_IMPORT_NAME = "test.Obj"
QML_IMPORT_MAJOR_VERSION = 1
+
@QmlElement
class Obj(QObject):
def __init__(self):
app = QApplication([])
file = Path(__file__).resolve().parent / 'bug_1060.ui'
- assert(file.is_file())
+ assert (file.is_file())
ui = MyQUiLoader().load(file)
ui.show()
super().__init__()
loader = QUiLoader()
file = Path(__file__).resolve().parent / 'bug_552.ui'
- assert(file.is_file())
+ assert (file.is_file())
widget = loader.load(os.fspath(file), self)
self.children = []
for child in widget.findChildren(QObject, None):
app = QApplication([])
loader = QUiLoader()
file = Path(__file__).resolve().parent / 'bug_552.ui'
-assert(file.is_file())
+assert (file.is_file())
file = QFile(file)
w = QWidget()
# An exception can't be thrown
# this used to cause a segfault because the old inject code used to destroy the parent layout
file = Path(__file__).resolve().parent / 'bug_958.ui'
- assert(file.is_file())
+ assert (file.is_file())
self._cw = lLoader.load(file, self)
self.setCentralWidget(self._cw)
treeWidget.insertTopLevelItems(0, items)
_iter = QTreeWidgetItemIterator(treeWidget)
index = 0
- while(_iter.value()):
+ while (_iter.value()):
item = _iter.value()
self.assertTrue(item is items[index])
index += 1
class Ball(QGraphicsEllipseItem):
def __init__(self, d, parent=None):
super().__init__(0, 0, d, d, parent)
- self.vel = QPointF(0, 0) #commenting this out prevents the crash
+ self.vel = QPointF(0, 0) # commenting this out prevents the crash
class Foo(QGraphicsView):
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QTableView, QVBoxLayout, QApplication
-from PySide6.QtCore import QAbstractItemModel
+from PySide6.QtWidgets import QTableView, QApplication
+from PySide6.QtCore import QAbstractItemModel, QModelIndex
from helper.usesqapplication import UsesQApplication
def __init__(self, app):
# Creates a new widget
- assert(app)
+ assert (app)
super().__init__()
self.app = app
# address that our QPixmap p1 because it was deleted
# using `del ret_p`
self.assertTrue(all(Shiboken.getCppPointer(o) != ret_p_addr
- for o in Shiboken.getAllValidWrappers()))
+ for o in Shiboken.getAllValidWrappers()))
# Test for PYSIDE-1673, QObject.property() returning a QFlags<> property.
def testQObjectProperty(self):
def testAddActionKeySequenceCallable(self):
# bug #228
action = self.menu.addAction(self.app.tr('aaa'), lambda: 1,
- QKeySequence(self.app.tr('Ctrl+O')))
+ QKeySequence(self.app.tr('Ctrl+O')))
def testAddActionKeySequenceSlot(self):
action = self.menu.addAction('Quit', self.app, SLOT('quit()'),
- QKeySequence('Ctrl+O'))
+ QKeySequence('Ctrl+O'))
class QMenuAddActionWithIcon(UsesQApplication):
def testAddActionKeySequenceCallable(self):
# bug #228
action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1,
- QKeySequence(self.app.tr('Ctrl+O')))
+ QKeySequence(self.app.tr('Ctrl+O')))
def testAddActionKeySequenceSlot(self):
action = self.menu.addAction(self.icon, 'Quit', self.app, SLOT('quit()'),
- QKeySequence('Ctrl+O'))
+ QKeySequence('Ctrl+O'))
if __name__ == '__main__':
from PySide6.QtGui import QWindow
from PySide6.QtWidgets import (QApplication, QFontComboBox, QLabel, QProxyStyle,
- QStyleFactory, QWidget)
+ QStyleFactory, QWidget)
class ProxyStyle(QProxyStyle):
from PySide6.QtGui import QStandardItemModel
from PySide6.QtWidgets import (QWidget, QTreeView, QVBoxLayout,
- QStyledItemDelegate, QHeaderView)
+ QStyledItemDelegate, QHeaderView)
from PySide6.QtCore import Qt
from helper.usesqapplication import UsesQApplication
from PySide6.QtCore import Qt, QObject
from PySide6.QtWidgets import (QComboBox, QGraphicsScene,
- QGraphicsRectItem)
+ QGraphicsRectItem)
from helper.usesqapplication import UsesQApplication
def testContainerTypes(self):
#list
- self.obj.setProperty("test", [1,2,3])
- self.assertEqual(self.obj.property("test"), [1,2,3])
+ self.obj.setProperty("test", [1, 2, 3])
+ self.assertEqual(self.obj.property("test"), [1, 2, 3])
self.assertTrue(isinstance(self.obj.property("test"), list))
#dict
self.obj.setProperty("test", {1: "one"})
def testCallConstructor(self):
label = QLabel(
frameStyle=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- lineWidth = 2, # QFrame property
+ lineWidth=2, # QFrame property
text="first line\nsecond line", # QLabel property
alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
)
label = QLabel(
frame_style=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- line_width = 2, # QFrame property
+ line_width=2, # QFrame property
text="first line\nsecond line", # QLabel property
alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
)
self.assertEqual(label.line_width(), 2)
self.assertRaises(AttributeError, lambda: QLabel(
- lineWidth = 2, # QFrame property
+ lineWidth=2, # QFrame property
))
# PYSIDE-1705: The same with true_property
label = QLabel(
frameStyle=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- lineWidth = 2, # QFrame property
+ lineWidth=2, # QFrame property
text="first line\nsecond line", # QLabel property
alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
)
self.assertEqual(label.lineWidth, 2)
self.assertRaises(AttributeError, lambda: QLabel(
- line_width = 2, # QFrame property
+ line_width=2, # QFrame property
))
# PYSIDE-1705: The same with snake_case and true_property
label = QLabel(
frame_style=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- line_width = 2, # QFrame property
+ line_width=2, # QFrame property
text="first line\nsecond line", # QLabel property
alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
)
self.assertEqual(label.line_width, 2)
self.assertRaises(AttributeError, lambda: QLabel(
- lineWidth = 2, # QFrame property
+ lineWidth=2, # QFrame property
))
self.assertEqual(sort_values(m2), EXPECTED_DICT)
def testList(self):
- l1 = ContainerTest.createList();
+ l1 = ContainerTest.createList()
self.assertEqual(l1, EXPECTED_LIST)
l2 = ContainerTest.passThroughList(l1)
self.assertEqual(l2, EXPECTED_LIST)
def testSet(self):
- s1 = ContainerTest.createSet(); # Order is not predictable
+ s1 = ContainerTest.createSet() # Order is not predictable
s2 = ContainerTest.passThroughSet(s1)
self.assertEqual(sorted(list(s1)), sorted(list(s2)))
import dis
+
class ListConnectionTest(unittest.TestCase):
def testEnumVisibility(self):
self.assertFalse(Qt.AlignBottom < Qt.AlignHCenter)
self.assertTrue(Qt.AlignBottom > Qt.AlignHCenter)
+
# PYSIDE-1735: We are testing that opcodes do what they are supposed to do.
# This is needed in the PyEnum forgiveness mode where we need
# to introspect the code if an Enum was called with no args.
return sorted(res, key=lambda x: (x[1], x[0]))
_sin = sys.implementation.name
+
@unittest.skipIf(hasattr(sys.flags, "nogil"), f"{_sin} has different opcodes")
def testByteCode(self):
import dis
for _ in range(times):
f()
- code_quicken(self.probe_function2, QUICKENING_WARMUP_DELAY-1)
+ code_quicken(self.probe_function2, QUICKENING_WARMUP_DELAY - 1)
self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_2)
self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_2)
('STORE_FAST', 125, 1),
('RETURN_CONST', 121, 0)]
-
self.assertEqual(self.read_code(self.probe_function1), result_1)
self.assertEqual(self.read_code(self.probe_function2), result_2)
def testHomonymousMultipleInheritance(self):
c = C()
- self.assertEqual(c.method(), "M::method") # okay
- self.assertEqual(c.signal(), "M::signal") # problem on PySide6 6.2.2
+ self.assertEqual(c.method(), "M::method") # okay
+ self.assertEqual(c.signal(), "M::signal") # problem on PySide6 6.2.2
self.assertEqual(type(c.signal), SignalInstance)
if "-v" in sys.argv:
print(*args, **kw)
+
# This is the original testcase of PYSIDE-1564
class Age(object):
def __init__(self, age=0, **kwds):
self.age = age
+
class Person(QtCore.QObject, Age):
def __init__(self, name, **kwds):
super().__init__(**kwds)
# More tests follow:
+
# mro ('C', 'A', 'QObject', 'Object', 'B', 'object')
class A(QtCore.QObject):
def __init__(self, anna=77, **kw):
super().__init__(**kw)
xprint('A: after init')
+
class B:
def __init__(self, otto=6, age=7, **kw):
xprint(f'B: before init kw = {kw}')
self.age = age
xprint('B: after init')
+
class C(A, B):
def __init__(self, **kw):
xprint(f'C: before init kw = {kw}')
super().__init__(**kw)
xprint('C: after init')
+
# mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
class D(QtGui.QCursor):
def __init__(self, anna=77, **kw):
super().__init__(**kw)
xprint('D: after init')
+
class E:
def __init__(self, age=7, **kw):
xprint(f'E: before init kw = {kw}')
self.age = age
xprint('E: after init')
+
class F(D, E, QtWidgets.QLabel):
def __init__(self, **kw):
xprint(f'F: before init kw = {kw}')
super().__init__(**kw)
xprint('F: after init')
+
# mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
# Similar, but this time we want to reach `H` without support from `super`.
class G(QtGui.QTextDocument):
pass
+
class H:
def __init__(self, age=7, **kw):
xprint(f'H: before init kw = {kw}')
self.age = age
xprint('H: after init')
+
class I(G, H, QtWidgets.QLabel):
pass
MainWindow.resize(400, 300)
self.lbl = QLabel(self)
+
class MainWindow(QMainWindow, Ui_X_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.assertEqual(res.age, 7)
xprint()
with self.assertRaises(AssertionError):
- res=C(killme=42)
+ res = C(killme=42)
xprint()
def testDEF(self):
finally:
os.unlink(fp.name)
+
def testprog2(option):
return runtest(dedent(f"""
sys.pyside6_option_python_enum = {option}
assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
"""))
+
def testprog4(option):
return runtest(dedent(f"""
sys.pyside6_option_python_enum = {option}
QtCore.QtDebugMsg
"""))
+
def testprog8_16(option):
# this test needs flag 16, or the effect would be hidden by forgiving mode
return runtest(dedent(f"""
QtCore.Qt.AlignTop
"""))
+
def testprog32(option):
return runtest(dedent(f"""
sys.pyside6_option_python_enum = {option}
QtCore.Qt.Alignment
"""))
+
def testprog64(option):
return runtest(dedent(f"""
sys.pyside6_option_python_enum = {option}
QtCore.Qt.AlignmentFlag()
"""))
+
def testprog128(option):
return runtest(dedent(f"""
sys.pyside6_option_python_enum = {option}
self.assertTrue(testprog4(12))
def test_localDefault(self):
- self.assertTrue(testprog8_16(8+16))
- self.assertFalse(testprog8_16(0+16))
+ self.assertTrue(testprog8_16(8 + 16))
+ self.assertFalse(testprog8_16(0 + 16))
def test_fakeRenames(self):
self.assertTrue(testprog32(1))
def test_custom_inherited_signal_instances_are_equal(self):
o = D()
self.assertTrue(o.custom_signal == o.custom_signal)
+
# additional tests of old errors from 2010 or so
def test_uninitialized_SignalInstance(self):
# This will no longer crash
with self.assertRaises(RuntimeError):
SignalInstance().emit()
+
class MyWidget(QSlider):
valueChanged = Signal(tuple)
from PySide6.QtWidgets import QWidget
+
def test_no_snake_case():
print(__name__)
widget = QWidget()
"""
PYSIDE-2029: Tests that snake_case is isolated from imported modules
"""
+is_pypy = hasattr(sys, "pypy_version_info")
from PySide6.QtCore import QSize
from PySide6.QtWidgets import QWidget, QSpinBox
-from __feature__ import snake_case
+if not is_pypy:
+ from __feature__ import snake_case
from helper.usesqapplication import UsesQApplication
import snake_case_sub
+@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
class SnakeCaseNoPropagateTest(UsesQApplication):
def testSnakeCase(self):
snake_case_sub.test_no_snake_case()
+
if __name__ == '__main__':
unittest.main()
"""
PYSIDE-2042: Tests true_property with inheritance
"""
+is_pypy = hasattr(sys, "pypy_version_info")
from PySide6.QtCore import QSize
from PySide6.QtWidgets import QWidget, QSpinBox
-from __feature__ import true_property
+if not is_pypy:
+ from __feature__ import true_property
from helper.usesqapplication import UsesQApplication
+@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
class TruePropertyInheritanceTest(UsesQApplication):
def testTrueProperty(self):
self.assertTrue(hasattr(widget, "setVisible"))
self.assertEqual(widget.isVisible, QWidget.visible.fget)
self.assertEqual(widget.setVisible, QWidget.visible.fset)
+
# This works with inheritance as well:
class SubClass(QWidget):
pass
print(os.getpid(), test_name)
response = read_url(sub_url)
txt = response.text if response else ''
- if "BEGIN_FILE" in txt and not "'BEGIN_FILE'" in txt:
+ if "BEGIN_FILE" in txt and "'BEGIN_FILE'" not in txt:
# find the text, but not a traceback with that text
print(os.getpid(), test_name, "FOUND!")
write_data(test_name, response.text)
if mod and hasattr(mod, warn_name):
save_warnings[name] = mod.__dict__[warn_name]
delattr(mod, warn_name)
- else:
- save_warnings[name] = None
yield
for name, warn in save_warnings.items():
mod = sys.modules[name]
def testIntegerCb(self):
# Test passing an int as callback to QObject.connect
self.assertRaises(TypeError, QObject.connect, self.obj,
- SIGNAL('destroyed()'), 42)
+ SIGNAL('destroyed()'), 42)
if __name__ == '__main__':
self.app.exec()
self.assertTrue(isinstance(recv.the_sender, QObject))
+ def testSenderCppSignalSingleShotTimerWithContext(self):
+ recv = Receiver()
+ QTimer.singleShot(10, recv, recv.callback)
+ self.app.exec()
+ self.assertTrue(isinstance(recv.the_sender, QObject))
+
def testSenderCppSignalWithPythonExtendedClass(self):
sender = ExtQTimer()
recv = Receiver()
if __name__ == '__main__':
unittest.main()
-
ignore = ["staticMetaObject",
"pyqtConfigure",
"registerUserData",
- "thread",
- ]
+ "thread"]
def recurse_into(el, obj):
return symbols
-if __name__=='__main__':
+if __name__ == '__main__':
modules = [ 'QtCore',
'QtGui',
'QtHelp',
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import unittest
-import tempfile
+import importlib
+import os
+import re
import shutil
import sys
-import os
-import importlib
+import tempfile
+import unittest
+import subprocess
from pathlib import Path
-from configparser import ConfigParser
-from unittest.mock import patch
from unittest import mock
+from unittest.mock import patch
-
-class ConfigFile:
- def __init__(self, config_file: Path) -> None:
- self.config_file = config_file
- self.parser = ConfigParser(comment_prefixes="/", allow_no_value=True)
- self.parser.read(self.config_file)
-
- def get_value(self, section: str, key: str):
- return str(self.parser.get(section, key))
+sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths
+init_test_paths(False)
-class TestPySide6AndroidDeploy(unittest.TestCase):
+class DeployTestBase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.pyside_root = Path(__file__).parents[5].resolve()
cls.example_root = cls.pyside_root / "examples"
- example_widget_application = cls.example_root / "gui" / "analogclock"
cls.temp_dir = tempfile.mkdtemp()
- cls.temp_example = Path(
- shutil.copytree(example_widget_application, Path(cls.temp_dir) / "analogclock")
- ).resolve()
cls.current_dir = Path.cwd()
- cls.pyside_wheel = Path("tmp/PySide6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
- cls.shiboken_wheel = Path("tmp/shiboken6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
- cls.ndk_path = Path("tmp/android_sdk/ndk/25.2.9519653")
- cls.sdk_path = Path("tmp/android_sdk")
-
- sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
+ cls.pyside_wheel = Path("/tmp/PySide6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
+ cls.shiboken_wheel = Path("/tmp/shiboken6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
+ cls.ndk_path = Path("/tmp/android_sdk/ndk/25.2.9519653")
+ cls.sdk_path = Path("/tmp/android_sdk")
+ pyside_tools = cls.pyside_root / "sources" / "pyside-tools"
+
+ # install extra python dependencies
+ android_requirements_file = pyside_tools / "requirements-android.txt"
+ with open(android_requirements_file, 'r', encoding='UTF-8') as file:
+ while line := file.readline():
+ dependent_package = line.rstrip()
+ if not bool(importlib.util.find_spec(dependent_package)):
+ command = [sys.executable, "-m", "pip", "install", dependent_package]
+ subprocess.run(command)
+
+ sys.path.append(str(pyside_tools))
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.android_deploy = importlib.import_module("android_deploy")
sys.modules["android_deploy"] = cls.android_deploy
# print no outputs to stdout
sys.stdout = mock.MagicMock()
+ def tearDown(self) -> None:
+ super().tearDown()
+ os.chdir(self.current_dir)
+
+ @classmethod
+ def tearDownClass(cls) -> None:
+ shutil.rmtree(Path(cls.temp_dir))
+
+
+@patch("deploy_lib.android.android_config.extract_and_copy_jar")
+class TestPySide6AndroidDeployWidgets(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_widget_application = cls.example_root / "gui" / "analogclock"
+ cls.temp_example = Path(
+ shutil.copytree(example_widget_application, Path(cls.temp_dir) / "analogclock")
+ ).resolve()
+
def setUp(self):
os.chdir(self.temp_example)
self.config_file = self.temp_example / "pysidedeploy.spec"
+ self.buildozer_config = self.temp_example / "buildozer.spec"
- @patch("android_deploy.extract_and_copy_jar")
- @patch("android_deploy.Wheel")
- def test_dry_run(self, mock_jar, mock_wheel):
- mock_wheel.version = "6.5.0a1"
-
- # test if dry_run works without errors
+ def test_dry_run(self, mock_extract_jar):
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, dry_run=True, force=True)
-
- self.assertEqual(mock_wheel.call_count, 1)
- self.assertEqual(mock_jar.call_count, 1)
- self.assertFalse(self.config_file.exists())
-
- @patch("android_deploy.extract_and_copy_jar")
- @patch("android_deploy.Wheel")
- def test_config(self, mock_jar, mock_wheel):
- '''
- Tests config options from the dynamically created buildozer.spec and pysidedeploy.spec
- '''
- mock_wheel.version = "6.5.0a1"
+ pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+ dry_run=True, force=True)
+ self.assertEqual(mock_extract_jar.call_count, 0)
+
+ @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+ @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+ @patch("deploy_lib.android.buildozer.BuildozerConfig."
+ "_BuildozerConfig__find_dependent_qt_modules")
+ @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+ def test_config(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist, mock_find_jars,
+ mock_extract_jar):
+ jar_dir = "tmp/jar/PySide6/jar"
+ mock_extract_jar.return_value = Path(jar_dir)
+ mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+ mock_extraqtmodules.return_value = []
+ mock_recipes_exist.return_value = True
+ jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+ "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar"], []
+ mock_find_jars.return_value = jars, init_classes
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, init=True, force=True)
+ pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+ init=True, force=True, keep_deployment_files=True)
- self.assertEqual(mock_wheel.call_count, 1)
- self.assertEqual(mock_jar.call_count, 1)
+ self.assertEqual(mock_extract_jar.call_count, 1)
+ self.assertEqual(mock_qtlibs.call_count, 1)
+ self.assertEqual(mock_extraqtmodules.call_count, 1)
+ self.assertEqual(mock_recipes_exist.call_count, 1)
+ self.assertEqual(mock_find_jars.call_count, 1)
self.assertTrue(self.config_file.exists())
+ self.assertTrue(self.buildozer_config.exists())
# test config file contents
- config_obj = ConfigFile(config_file=self.config_file)
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
self.assertEqual(config_obj.get_value("python", "android_packages"),
"buildozer==1.5.0,cython==0.29.33")
- self.assertEqual(config_obj.get_value("qt", "wheel_pyside"),
+ self.assertEqual(config_obj.get_value("android", "wheel_pyside"),
str(self.pyside_wheel.resolve()))
- self.assertEqual(config_obj.get_value("qt", "wheel_shiboken"),
+ self.assertEqual(config_obj.get_value("android", "wheel_shiboken"),
str(self.shiboken_wheel.resolve()))
self.assertEqual(config_obj.get_value("buildozer", "mode"), "debug")
self.assertEqual(config_obj.get_value("buildozer", "recipe_dir"),
- str(self.temp_example / "deployment" / "recipes"))
+ '')
self.assertEqual(config_obj.get_value("buildozer", "jars_dir"),
- str(self.temp_example / "deployment" / "jar" / "PySide6" / "jar"))
- self.assertEqual(config_obj.get_value("buildozer", "ndk_path"), "")
- self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), "")
- self.assertEqual(config_obj.get_value("buildozer", "modules"), "Core,Gui,Widgets")
+ str(self.temp_example / jar_dir))
+ self.assertIn(str(self.ndk_path), config_obj.get_value("buildozer", "ndk_path"))
+ self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '')
+ expected_modules = {"Core", "Gui"}
+ obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ expected_local_libs = "plugins_platforms_qtforandroid"
self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
- "plugins_platforms_qtforandroid")
+ expected_local_libs)
self.assertEqual(config_obj.get_value("buildozer", "arch"), "x86_64")
- self.config_file.unlink()
-
- @patch("android_deploy.extract_and_copy_jar")
- @patch("android_deploy.Wheel")
- def test_config_with_ndk_sdk(self, mock_jar, mock_wheel):
- mock_wheel.version = "6.5.0a1"
- self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
- sdk_path=self.sdk_path, init=True, force=True)
-
- self.assertEqual(mock_wheel.call_count, 1)
- self.assertEqual(mock_jar.call_count, 1)
- self.assertTrue(self.config_file.exists())
+ # test buildozer config file contents
+ buildozer_config_obj = self.deploy_lib.BaseConfig(config_file=self.buildozer_config)
+ obtained_jars = set(buildozer_config_obj.get_value("app", "android.add_jars").split(','))
+ expected_jars = set(jars)
+ self.assertEqual(obtained_jars, expected_jars)
+ obtained_extra_args = buildozer_config_obj.get_value("app", "p4a.extra_args")
+ extra_args_patrn = re.compile("--qt-libs=(?P<modules>.*) --load-local-libs="
+ "(?P<local_libs>.*) --init-classes=(?P<init_classes>.*)")
+ match = extra_args_patrn.search(obtained_extra_args)
+ obtained_modules = match.group("modules").split(',')
+ obtained_local_libs = match.group("local_libs")
+ obtained_init_classes = match.group("init_classes")
+ self.assertEqual(set(obtained_modules), expected_modules)
+ self.assertEqual(obtained_local_libs, expected_local_libs)
+ self.assertEqual(obtained_init_classes, '')
+ expected_include_exts = "py,png,jpg,kv,atlas,qml,js"
+ obtained_include_exts = buildozer_config_obj.get_value("app", "source.include_exts")
+ self.assertEqual(expected_include_exts, obtained_include_exts)
- # test config file contents
- config_obj = ConfigFile(config_file=self.config_file)
- self.assertEqual(config_obj.get_value("buildozer", "ndk_path"),
- str(self.ndk_path.resolve()))
- self.assertEqual(config_obj.get_value("buildozer", "sdk_path"),
- str(self.sdk_path.resolve()))
self.config_file.unlink()
+ self.buildozer_config.unlink()
- def test_error_pwd_not_projectdir(self):
- os.chdir(self.current_dir)
- with self.assertRaises(RuntimeError):
+ def test_errors(self, mock_extract_jar):
+ # test if error raises for non existing NDK
+ with self.assertRaises(FileNotFoundError) as context:
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, init=True, force=True)
+ pyside_wheel=self.pyside_wheel, force=True)
+ self.assertTrue("Unable to find Android NDK" in str(context.exception))
- def test_error_no_wheels(self):
+ # test when cwd() is not project_dir
os.chdir(self.current_dir)
- with self.assertRaises(RuntimeError):
- self.android_deploy.main(name="android_app", shiboken_wheel=None,
+ with self.assertRaises(RuntimeError) as context:
+ self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
pyside_wheel=self.pyside_wheel, init=True, force=True)
+ self.assertTrue("For Android deployment to work" in str(context.exception))
- @patch("android_deploy.extract_and_copy_jar")
- @patch("android_deploy.Wheel")
- def test_config_with_Qml(self, mock_jar, mock_wheel):
- example_qml_application = self.example_root / "quick" / "models" / "stringlistmodel"
- temp_qml_example = Path(
- shutil.copytree(example_qml_application, Path(self.temp_dir) / "stringlistmodel")
+
+@patch("deploy_lib.config.run_qmlimportscanner")
+@patch("deploy_lib.android.android_config.extract_and_copy_jar")
+class TestPySide6AndroidDeployQml(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ # setting up example
+ example_qml_application = cls.example_root / "quick" / "models" / "stringlistmodel"
+ cls.temp_qml_example = Path(
+ shutil.copytree(example_qml_application, Path(cls.temp_dir) / "stringlistmodel")
).resolve()
- config_file = temp_qml_example / "pysidedeploy.spec"
- (temp_qml_example / "stringlistmodel.py").rename(temp_qml_example / "main.py")
- (temp_qml_example / "stringlistmodel.pyproject").unlink()
- os.chdir(temp_qml_example)
- mock_wheel.version = "6.5.0a1"
+ def setUp(self):
+ os.chdir(self.temp_qml_example)
+ self.config_file = self.temp_qml_example / "pysidedeploy.spec"
+ self.buildozer_config_file = self.temp_qml_example / "buildozer.spec"
+ (self.temp_qml_example / "stringlistmodel.py").rename(self.temp_qml_example / "main.py")
+ (self.temp_qml_example / "stringlistmodel.pyproject").unlink()
+
+ @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_local_libs")
+ @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+ @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+ @patch("deploy_lib.android.buildozer.BuildozerConfig."
+ "_BuildozerConfig__find_dependent_qt_modules")
+ @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+ def test_config_with_Qml(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist,
+ mock_find_jars, mock_local_libs, mock_extract_jar,
+ mock_qmlimportscanner):
+ # setting up mocks
+ jar_dir = "tmp/jar/PySide6/jar"
+ mock_extract_jar.return_value = Path(jar_dir)
+ mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+ mock_extraqtmodules.return_value = ['Qml', 'Network', 'QmlModels', 'OpenGL']
+ mock_recipes_exist.return_value = True
+ jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+ "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar",
+ "/tmp/jar/PySide6/jar/Qt6AndroidNetworkInformationBackend.jar",
+ "/tmp/jar/PySide6/jar/Qt6AndroidNetwork.jar"], []
+ mock_find_jars.return_value = jars, init_classes
+ dependent_plugins = ["platforms_qtforandroid",
+ "platforminputcontexts_qtvirtualkeyboardplugin",
+ "iconengines_qsvgicon"]
+ mock_local_libs.return_value = [], dependent_plugins
+ mock_qmlimportscanner.return_value = ["QtQuick"]
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, init=True, force=True)
-
- self.assertEqual(mock_wheel.call_count, 1)
- self.assertEqual(mock_jar.call_count, 1)
- self.assertTrue(config_file.exists())
+ pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+ init=True, force=True, keep_deployment_files=True)
+
+ self.assertEqual(mock_extract_jar.call_count, 1)
+ self.assertEqual(mock_qtlibs.call_count, 1)
+ self.assertEqual(mock_extraqtmodules.call_count, 1)
+ self.assertEqual(mock_recipes_exist.call_count, 1)
+ self.assertEqual(mock_find_jars.call_count, 1)
+ self.assertEqual(mock_qmlimportscanner.call_count, 1)
+ self.assertTrue(self.config_file.exists())
+ self.assertTrue(self.buildozer_config_file.exists())
# test config file contents
- config_obj = ConfigFile(config_file=config_file)
- self.assertEqual(config_obj.get_value("buildozer", "modules"),
- "Core,Gui,Widgets,Network,OpenGL,Qml,Quick,QuickControls2")
- config_file.unlink()
-
- def tearDown(self) -> None:
- super().tearDown()
- os.chdir(self.current_dir)
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+ expected_modules = {"Quick", "Core", "Gui", "Network", "Qml", "QmlModels", "OpenGL"}
+ obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ expected_local_libs = "plugins_platforms_qtforandroid"
+ self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
+ expected_local_libs)
+ expected_qt_plugins = set(dependent_plugins)
+ obtained_qt_plugins = set(config_obj.get_value("android", "plugins").split(","))
+ self.assertEqual(expected_qt_plugins, obtained_qt_plugins)
+
+ # test buildozer config file contents
+ buildozer_config_obj = self.deploy_lib.BaseConfig(config_file=self.buildozer_config_file)
+ obtained_jars = set(buildozer_config_obj.get_value("app", "android.add_jars").split(','))
+ expected_jars = set(jars)
+ self.assertEqual(obtained_jars, expected_jars)
+ obtained_extra_args = buildozer_config_obj.get_value("app", "p4a.extra_args")
+ extra_args_patrn = re.compile("--qt-libs=(?P<modules>.*) --load-local-libs="
+ "(?P<local_libs>.*) --init-classes=(?P<init_classes>.*)")
+ match = extra_args_patrn.search(obtained_extra_args)
+ obtained_modules = match.group("modules").split(',')
+ obtained_local_libs = match.group("local_libs")
+ obtained_init_classes = match.group("init_classes")
+ self.assertEqual(set(obtained_modules), expected_modules)
+ self.assertEqual(obtained_local_libs, expected_local_libs)
+ self.assertEqual(obtained_init_classes, '')
- @classmethod
- def tearDownClass(cls) -> None:
- shutil.rmtree(Path(cls.temp_dir))
+ self.config_file.unlink()
+ self.buildozer_config_file.unlink()
if __name__ == "__main__":
keep_deployment_files=True, force=True)
print("Now testing Widget with config file")
- self.deploy.main(self.main_file, config_file=self.config_file, loglevel=logging.INFO,
+ self.deploy.main(self.main_file, config_file=self.config_file, loglevel=logging.INFO,
force=True)
def setUpQml(self):
import os
import importlib
from pathlib import Path
-from configparser import ConfigParser
from unittest.mock import patch
from unittest import mock
-class ConfigFile:
- def __init__(self, config_file: Path) -> None:
- self.config_file = config_file
- self.parser = ConfigParser(comment_prefixes="/", allow_no_value=True)
- self.parser.read(self.config_file)
+def is_pyenv_python():
+ pyenv_root = os.environ.get("PYENV_ROOT")
- def get_value(self, section: str, key: str):
- return str(self.parser.get(section, key))
+ if pyenv_root and (resolved_exe := str(Path(sys.executable).resolve())):
+ return resolved_exe.startswith(pyenv_root)
+ return False
-class TestPySide6Deploy(unittest.TestCase):
+class LongSortedOptionTest(unittest.TestCase):
+ @staticmethod
+ def _option_prepare(s):
+ """
+ Take a string and return a list obtained by text.split().
+ Options starting with "--" are also sorted."
+ """
+ items = s.split()
+ for idx in range(len(items)):
+ if items[idx].startswith("--"):
+ return items[:idx] + sorted(items[idx:])
+ return items
+
+ def assertEqual(self, text_a, text_b):
+ if (not isinstance(text_a, str) or not isinstance(text_b, str)
+ or (len(text_a) < 50 and len(text_b) < 50)):
+ return super().assertEqual(text_a, text_b)
+ sort_a = self._option_prepare(text_a)
+ sort_b = self._option_prepare(text_b)
+ return super().assertEqual(sort_a, sort_b)
+
+
+class DeployTestBase(LongSortedOptionTest):
@classmethod
def setUpClass(cls):
- # PYSIDE-2230: A temporary patch that avoids the pyenv error.
- # The final solution is too much for this quick fix.
- if os.environ.get("PYENV_ROOT"):
- del os.environ["PYENV_ROOT"]
cls.pyside_root = Path(__file__).parents[5].resolve()
- example_root = cls.pyside_root / "examples"
- example_widgets = example_root / "widgets" / "widgets" / "tetrix"
- example_qml = example_root / "qml" / "editingmodel"
- example_webenginequick = example_root / "webenginequick" / "nanobrowser"
+ cls.example_root = cls.pyside_root / "examples"
cls.temp_dir = tempfile.mkdtemp()
- cls.temp_example_widgets = Path(
- shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
- ).resolve()
- cls.temp_example_qml = Path(
- shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
- ).resolve()
- cls.temp_example_webenginequick = Path(
- shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
- ).resolve()
cls.current_dir = Path.cwd()
- cls.linux_onefile_icon = (
- cls.pyside_root / "sources" / "pyside-tools" / "deploy_lib" / "pyside_icon.jpg"
- )
-
- sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
+ tools_path = cls.pyside_root / "sources" / "pyside-tools"
+ cls.win_icon = tools_path / "deploy_lib" / "pyside_icon.ico"
+ cls.linux_icon = tools_path / "deploy_lib" / "pyside_icon.jpg"
+ cls.macos_icon = tools_path / "deploy_lib" / "pyside_icon.icns"
+ if tools_path not in sys.path:
+ sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.deploy = importlib.import_module("deploy")
sys.modules["deploy"] = cls.deploy
# print no outputs to stdout
sys.stdout = mock.MagicMock()
- def setUpWidgets(self):
+ @classmethod
+ def tearDownClass(cls) -> None:
+ shutil.rmtree(Path(cls.temp_dir))
+
+ def tearDown(self) -> None:
+ super().tearDown()
+ os.chdir(self.current_dir)
+
+
+class TestPySide6DeployWidgets(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
+ cls.temp_example_widgets = Path(
+ shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
+ ).resolve()
+
+ def setUp(self):
os.chdir(self.temp_example_widgets)
self.main_file = self.temp_example_widgets / "tetrix.py"
self.deployment_files = self.temp_example_widgets / "deployment"
self.expected_run_cmd = (
f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
- f" --noinclude-qt-translations=True"
+ f" --noinclude-qt-translations"
)
if sys.platform.startswith("linux"):
- self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+ self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ elif sys.platform == "darwin":
+ self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ elif sys.platform == "win32":
+ self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+
+ if is_pyenv_python():
+ self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_widgets / "pysidedeploy.spec"
def testWidgetDryRun(self):
# Checking for dry run commands is equivalent to mocking the
# subprocess.check_call() in commands.py as the the dry run command
# is the command being run.
- self.setUpWidgets()
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
def testWidgetConfigFile(self):
# includes both dry run and config_file tests
- self.setUpWidgets()
# init
init_result = self.deploy.main(self.main_file, init=True, force=True)
self.assertEqual(init_result, None)
self.assertEqual(original_output, self.expected_run_cmd)
# # test config file contents
- config_obj = ConfigFile(config_file=self.config_file)
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
self.assertEqual(config_obj.get_value("app", "input_file"), "tetrix.py")
self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
- self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
+ self.assertEqual(config_obj.get_value("python", "packages"),
+ "nuitka==1.8.0,ordered_set,zstandard")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
- self.assertEqual(
- config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
- )
+ equ_base = "--quiet --noinclude-qt-translations"
+ equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+ self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
self.assertEqual(config_obj.get_value("qt", "excluded_qml_plugins"), "")
self.config_file.unlink()
- def setUpQml(self):
+ def testErrorReturns(self):
+ # main file and config file does not exists
+ fake_main_file = self.main_file.parent / "main.py"
+ with self.assertRaises(RuntimeError) as context:
+ self.deploy.main(main_file=fake_main_file, config_file=self.config_file)
+ self.assertTrue("Directory does not contain main.py file." in str(context.exception))
+
+
+class TestPySide6DeployQml(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_qml = cls.example_root / "qml" / "editingmodel"
+ cls.temp_example_qml = Path(
+ shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
+ ).resolve()
+
+ def setUp(self):
os.chdir(self.temp_example_qml)
self.main_file = self.temp_example_qml / "main.py"
self.deployment_files = self.temp_example_qml / "deployment"
self.expected_run_cmd = (
f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
- f" --noinclude-qt-translations=True --include-qt-plugins=all"
+ f" --noinclude-qt-translations --include-qt-plugins=all"
f" --include-data-files={str(self.temp_example_qml / self.first_qml_file)}="
f"./main.qml --include-data-files="
f"{str(self.temp_example_qml /self.second_qml_file)}=./MovingRectangle.qml"
- )
+ )
if sys.platform != "win32":
self.expected_run_cmd += (
" --noinclude-dlls=libQt6Charts*"
" --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
" --noinclude-dlls=libQt6Test* --noinclude-dlls=libQt6WebEngine*"
- )
+ )
else:
self.expected_run_cmd += (
" --noinclude-dlls=Qt6Charts*"
" --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
" --noinclude-dlls=Qt6Test* --noinclude-dlls=Qt6WebEngine*"
- )
+ )
if sys.platform.startswith("linux"):
- self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+ self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ elif sys.platform == "darwin":
+ self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ elif sys.platform == "win32":
+ self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+
+ if is_pyenv_python():
+ self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_qml / "pysidedeploy.spec"
def testQmlConfigFile(self):
- self.setUpQml()
-
# create config file
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
self.assertEqual(init_result, None)
# test config file contents
- config_obj = ConfigFile(config_file=self.config_file)
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
- self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
+ self.assertEqual(config_obj.get_value("python", "packages"),
+ "nuitka==1.8.0,ordered_set,zstandard")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
- self.assertEqual(
- config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
- )
+ equ_base = "--quiet --noinclude-qt-translations"
+ equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+ self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
self.assertEqual(
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
self.config_file.unlink()
def testQmlDryRun(self):
- self.setUpQml()
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
self.assertEqual(mock_qmlimportscanner.call_count, 1)
def testMainFileDryRun(self):
- self.setUpQml()
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(Path.cwd() / "main.py", dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
self.assertEqual(mock_qmlimportscanner.call_count, 1)
+
+class TestPySide6DeployWebEngine(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_webenginequick = cls.example_root / "webenginequick" / "nanobrowser"
+ cls.temp_example_webenginequick = Path(
+ shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
+ ).resolve()
+
# this test case retains the QtWebEngine dlls
def testWebEngineQuickDryRun(self):
# setup
expected_run_cmd = (
f"{sys.executable} -m nuitka {str(main_file)} --follow-imports --onefile"
f" --enable-plugin=pyside6 --output-dir={str(deployment_files)} --quiet"
- f" --noinclude-qt-translations=True --include-qt-plugins=all"
+ f" --noinclude-qt-translations --include-qt-plugins=all"
f" {data_files_cmd}"
)
" --noinclude-dlls=libQt6Charts*"
" --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
" --noinclude-dlls=libQt6Test*"
- )
+ )
else:
expected_run_cmd += (
" --noinclude-dlls=Qt6Charts*"
" --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
" --noinclude-dlls=Qt6Test*"
- )
+ )
if sys.platform.startswith("linux"):
- expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+ expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ elif sys.platform == "darwin":
+ expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ elif sys.platform == "win32":
+ expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
config_file = self.temp_example_webenginequick / "pysidedeploy.spec"
self.assertEqual(mock_qmlimportscanner.call_count, 2)
# test config file contents
- config_obj = ConfigFile(config_file=config_file)
+ config_obj = self.deploy_lib.BaseConfig(config_file=config_file)
self.assertEqual(config_obj.get_value("app", "input_file"), "quicknanobrowser.py")
self.assertEqual(config_obj.get_value("qt", "qml_files"), ",".join(qml_files))
self.assertEqual(
"QtCharts,QtQuick3D,QtSensors,QtTest",
)
- def tearDown(self) -> None:
- super().tearDown()
- os.chdir(self.current_dir)
-
- @classmethod
- def tearDownClass(cls) -> None:
- shutil.rmtree(Path(cls.temp_dir))
-
if __name__ == "__main__":
unittest.main()
doc_filter = lambda x: x.startswith('test')
doc_suffix = 'suffix'
- assert(Implementing.testBase.__doc__ == 'prefixbasesuffix')
- assert(Implementing.testWithoutDoc.__doc__ == None)
- assert(OnlySuffix.testBase.__doc__ == 'basesuffix')
- assert(OnlySuffix.testWithoutDoc.__doc__ == None)
- assert(OnlyPrefix.testBase.__doc__ == 'prefixbase')
- assert(OnlyPrefix.testWithoutDoc.__doc__ == None)
+ assert (Implementing.testBase.__doc__ == 'prefixbasesuffix')
+ assert (Implementing.testWithoutDoc.__doc__ == None)
+ assert (OnlySuffix.testBase.__doc__ == 'basesuffix')
+ assert (OnlySuffix.testWithoutDoc.__doc__ == None)
+ assert (OnlyPrefix.testBase.__doc__ == 'prefixbase')
+ assert (OnlyPrefix.testWithoutDoc.__doc__ == None)
# This should work with every compatible library.
# Replaces the QtGui and QtCore versions as well.
+
class UsesQApplication(unittest.TestCase):
'''Helper class to provide Q(Core|Gui|)Application instances
Just connect or call self.exit_app_cb. When called, will ask
def waitfor(self):
time_passed = 0
- while(self.proc.poll() is None and time_passed < self.timeout):
+ while (self.proc.poll() is None and time_passed < self.timeout):
time_passed = time_passed + 1
time.sleep(1)
set(shiboken_MAJOR_VERSION "6")
set(shiboken_MINOR_VERSION "6")
-set(shiboken_MICRO_VERSION "1")
+set(shiboken_MICRO_VERSION "2")
set(shiboken_PRE_RELEASE_VERSION_TYPE "")
set(shiboken_PRE_RELEASE_VERSION "")
if (!currentClass || currentClass->typeEntry()->generateCode()) {
const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
qCWarning(lcShiboken, "%s",
- qPrintable(msgStrippingArgument(functionItem, i, signature, arg)));
+ qPrintable(msgStrippingArgument(functionItem, i, signature,
+ arg, errorMessage)));
}
break;
}
{
QString result = m_originalName + u'(';
for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+ const auto &argument = m_arguments.at(i);
if (i > 0)
result += u',';
- result += comment
- ? m_arguments.at(i).modifiedType().minimalSignature()
- : m_arguments.at(i).type().minimalSignature();
+ const auto &type = comment ? argument.modifiedType() : argument.type();
+ result += type.minimalSignature();
+ if (comment && argument.hasDefaultValueExpression())
+ result += u'=';
}
result += u')';
if (m_constant)
}
}
+static inline bool isSignal(const AbstractMetaFunctionCPtr &f)
+{
+ return f->isSignal();
+}
+
void AbstractMetaClass::fixFunctions(const AbstractMetaClassPtr &klass)
{
auto *d = klass->d.data();
*superClass -= AbstractMetaClass::FinalInTargetLang;
}
superFuncs = superClass->queryFunctions(FunctionQueryOption::ClassImplements);
+ // We are not interested in signals as no bindings are generated for them;
+ // they cause documentation warnings.
+ superFuncs.erase(std::remove_if(superFuncs.begin(), superFuncs.end(), isSignal),
+ superFuncs.end());
const auto virtuals = superClass->queryFunctions(FunctionQueryOption::VirtualInCppFunctions);
superFuncs += virtuals;
#include "messages.h"
#include "typedatabase.h"
#include "containertypeentry.h"
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
#include "qtcompat.h"
#include "typeinfo.h"
if (m_typeEntry->isPrimitive())
for (Indirection i : m_indirections)
result += TypeInfo::indirectionKeyword(i);
- // If it is a flags type, we replace it with the full name:
- // "PySide6.QtCore.Qt.ItemFlags" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
- if (m_typeEntry->isFlags())
- result = m_typeEntry->qualifiedTargetLangName();
+ // If it is a flags type, we replace it with the full name of the enum:
+ // "PySide6.QtCore.Qt.ItemFlag" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
+ if (m_typeEntry->isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(m_typeEntry);
+ result = fte->originator()->qualifiedTargetLangName();
+ }
result.replace(u"::"_s, u"."_s);
return result;
}
#include "typedatabase_typedefs.h"
#include <QtCore/qobjectdefs.h>
+#include <QtCore/QHashFunctions>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QList>
#include <QtCore/QSet>
#endif
private:
+ friend size_t qHash(const AbstractMetaType &t, size_t seed = 0) noexcept
+ { return qHash(t.typeEntry().get(), seed); }
+
friend class AbstractMetaTypeData;
QSharedDataPointer<AbstractMetaTypeData> d;
inline bool operator!=(const AbstractMetaType &t1, const AbstractMetaType &t2)
{ return !t1.equals(t2); }
-inline size_t qHash(const AbstractMetaType &t, size_t seed)
-{ return qHash(t.typeEntry().get(), seed); }
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const AbstractMetaType &at);
QDebug operator<<(QDebug d, const AbstractMetaType *at);
#include <string_view>
-bool operator==(const CXCursor &c1, const CXCursor &c2)
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept
{
return c1.kind == c2.kind
&& c1.xdata == c2.xdata
&& std::equal(c1.data, c1.data + sizeof(c1.data) / sizeof(c1.data[0]), c2.data);
}
-size_t qHash(const CXCursor &c, size_t seed)
+size_t qHash(const CXCursor &c, size_t seed) noexcept
{
- return qHash(c.kind) ^ qHash(c.xdata) ^ qHash(c.data[0])
- ^ qHash(c.data[1]) ^ qHash(c.data[2]) ^ seed;
+ return qHashMulti(seed, c.kind, c.xdata, c.data[0], c.data[1], c.data[2]);
}
-bool operator==(const CXType &t1, const CXType &t2)
+bool operator==(const CXType &t1, const CXType &t2) noexcept
{
return t1.kind == t2.kind && t1.data[0] == t2.data[0]
&& t1.data[1] == t2.data[1];
}
-size_t qHash(const CXType &ct, size_t seed)
+size_t qHash(const CXType &ct, size_t seed) noexcept
{
- return size_t(ct.kind) ^ size_t(0xFFFFFFFF & quintptr(ct.data[0]))
- ^ size_t(0xFFFFFFFF & quintptr(ct.data[1])) ^ seed;
+ return qHashMulti(seed, ct.kind, ct.data[0], ct.data[1]);
}
namespace clang {
QT_FORWARD_DECLARE_CLASS(QDebug)
-bool operator==(const CXCursor &c1, const CXCursor &c2);
-size_t qHash(const CXCursor &c, size_t seed = 0);
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept;
+size_t qHash(const CXCursor &c, size_t seed = 0) noexcept;
-bool operator==(const CXType &t1, const CXType &t2);
-size_t qHash(const CXType &ct, size_t seed);
+bool operator==(const CXType &t1, const CXType &t2) noexcept;
+size_t qHash(const CXType &ct, size_t seed = 0) noexcept;
namespace clang {
+ func->originalName() + u"\"]"_s;
if (func->arguments().isEmpty()) {
- QString args = func->isConstant() ? u"() const "_s : u"()"_s;
+ QString args = func->isConstant() ? u"() const"_s : u"()"_s;
query += u"/../argsstring[text()=\""_s + args + u"\"]"_s;
} else {
int i = 1;
return m_name.compare(rhs.m_name);
}
-size_t qHash(const Include& inc)
-{
- return qHash(inc.m_name);
-}
-
QTextStream& operator<<(QTextStream& out, const Include& g)
{
if (g.isValid())
#ifndef INCLUDE_H
#define INCLUDE_H
+#include <QtCore/QHashFunctions>
#include <QtCore/QString>
#include <QtCore/QList>
QString toString() const;
- friend size_t qHash(const Include &);
int compare(const Include &rhs) const;
- private:
- IncludeType m_type = IncludePath;
- QString m_name;
-};
+private:
+ friend size_t qHash(Include &inc, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, inc.m_type, inc.m_name);
+ }
-size_t qHash(const Include& inc);
+ IncludeType m_type = IncludePath;
+ QString m_name;
+};
inline bool operator<(const Include &lhs, const Include &rhs)
{
QString msgStrippingArgument(const FunctionModelItem &f, int i,
const QString &originalSignature,
- const ArgumentModelItem &arg)
+ const ArgumentModelItem &arg,
+ const QString &reason)
{
QString result;
QTextStream str(&result);
str << f->sourceLocation() << "Stripping argument #" << (i + 1) << " of "
<< originalSignature << " due to unmatched type \""
<< arg->type().toString() << "\" with default expression \""
- << arg->defaultValueExpression() << "\".";
+ << arg->defaultValueExpression() << "\": " << reason;
return result;
}
return result;
}
+QString msgSnippetError(const QString &context, const char *what)
+{
+ return "Error processing code snippet of "_L1 + context
+ + ": "_L1 + QString::fromUtf8(what);
+}
+
QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName)
{
QString result;
QString msgStrippingArgument(const FunctionModelItem &f, int i,
const QString &originalSignature,
- const ArgumentModelItem &arg);
+ const ArgumentModelItem &arg,
+ const QString &reason);
QString msgEnumNotDefined(const EnumTypeEntryCPtr &t);
QString msgCannotFindView(const QString &viewedName, const QString &name);
QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel);
+QString msgSnippetError(const QString &context, const char *what);
QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName);
QString msgCyclicDependency(const QString &funcName, const QString &graphName,
macro(set_debug_build)
set(SHIBOKEN_BUILD_TYPE "Debug")
- if(NOT PYTHON_DEBUG_LIBRARIES)
+ if(NOT Python_LIBRARIES)
message(WARNING "Python debug shared library not found; \
assuming python was built with shared library support disabled.")
endif()
endif()
else()
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+ COMMAND ${Python_EXECUTABLE} -c "if True:
import sysconfig
from os.path import sep
# Python_SOABI is only set by CMake 3.17+
# TODO: Lower this to CMake 3.16 if possible.
if(SHIBOKEN_IS_CROSS_BUILD)
+ # For android platform armv7a FindPython module return Python_SOABI as empty because
+ # it is unable to set Python_CONFIG i.e. find `python3-config` script
+ # This workaround sets the Python_SOABI manually for this platform.
+ if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
+ set(Python_SOABI "cpython-310}")
+ endif()
if(NOT Python_SOABI)
message(FATAL_ERROR "Python_SOABI variable is empty.")
endif()
else()
# See PYSIDE-1841 / https://bugs.python.org/issue39825 for distutils vs sysconfig
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+ COMMAND ${Python_EXECUTABLE} -c "if True:
import sys
if sys.version_info >= (3, 8, 2):
import sysconfig
# TODO: Figure out how to use limited API libs when cross-building to Windows, if that's ever
# needed. Perhaps use host python to walk the libs of the target python installation.
- if(NOT SHIBOKEN_IS_CROSS_BUILD)
+ if(NOT SHIBOKEN_IS_CROSS_BUILD AND WIN32)
# On Windows, PYTHON_LIBRARIES can be a list. Example:
# optimized;C:/Python36/libs/python36.lib;debug;C:/Python36/libs/python36_d.lib
# On other platforms, this result is not used at all.
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
- import os
- for lib in '${PYTHON_LIBRARIES}'.split(';'):
- if '/' in lib and os.path.isfile(lib):
- prefix, py = lib.rsplit('/', 1)
+ COMMAND ${Python_EXECUTABLE} -c "if True:
+ from pathlib import Path
+ libs = r'${Python_LIBRARIES}'
+ libs = libs.split(';')
+ for lib in libs:
+ if '\\\\' in lib and Path(lib).is_file():
+ lib = Path(lib)
+ prefix = lib.parent
+ py = lib.name
if py.startswith('python3'):
- print(prefix + '/python3.lib')
+ print(prefix / 'python3.lib')
break
"
OUTPUT_VARIABLE PYTHON_LIMITED_LIBRARIES
macro(shiboken_find_required_python)
+ set(_shiboken_find_python_version_args "")
+ if(${ARGC} GREATER 0)
+ list(APPEND _shiboken_find_python_version_args "${ARGV0}")
+ endif()
# This function can also be called by consumers of ShibokenConfig.cmake package like pyside,
# that's why we also check for PYSIDE_IS_CROSS_BUILD (which is set by pyside project)
# and QFP_FIND_NEW_PYTHON_PACKAGE for an explicit opt in.
# We have to use FindPython package instead of FindPythonInterp to get required target Python
# information.
if(SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD OR QFP_FIND_NEW_PYTHON_PACKAGE)
- set(_shiboken_find_python_version_args "")
- if(${ARGC} GREATER 0)
- list(APPEND _shiboken_find_python_version_args "${ARGV0}")
- endif()
-
# We want FindPython to look in the sysroot for the python-config executable,
# but toolchain files might set CMAKE_FIND_ROOT_PATH_MODE_PROGRAM to NEVER because
# programs are mostly found for running and you usually can't run a target executable on
"${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}")
set(CMAKE_FIND_ROOT_PATH
"${_shiboken_backup_CMAKE_FIND_ROOT_PATH}")
-
- # Mirror the variables that FindPythonInterp sets, instead of conditionally checking
- # and modifying all the places where the variables are used.
- set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
- set(PYTHON_VERSION "${Python_VERSION}")
- set(PYTHON_LIBRARIES "${Python_LIBRARIES}")
- set(PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}")
- set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}")
- set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}")
- set(PYTHONLIBS_FOUND "${Python_Development_FOUND}")
- set(PYTHON_VERSION_MAJOR "${Python_VERSION_MAJOR}")
- set(PYTHON_VERSION_MINOR "${Python_VERSION_MINOR}")
- set(PYTHON_VERSION_PATCH "${Python_VERSION_PATCH}")
else()
- if(${ARGC} GREATER 0)
- find_package(PythonInterp ${ARGV0} REQUIRED)
- find_package(PythonLibs ${ARGV0} REQUIRED)
- else()
- # If no version is specified, just use any interpreter that can be found (from PATH).
- # This is useful for super-project builds, so that the default system interpeter
- # gets picked up (e.g. /usr/bin/python and not /usr/bin/python2.7).
- find_package(PythonInterp REQUIRED)
- find_package(PythonLibs REQUIRED)
- endif()
+ find_package(
+ Python
+ ${_shiboken_find_python_version_args}
+ REQUIRED
+ COMPONENTS Interpreter Development
+ )
endif()
shiboken_validate_python_version()
- set(SHIBOKEN_PYTHON_INTERPRETER "${PYTHON_EXECUTABLE}")
- set_property(GLOBAL PROPERTY SHIBOKEN_PYTHON_INTERPRETER "${PYTHON_EXECUTABLE}")
+ set(SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}")
+ set_property(GLOBAL PROPERTY SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}")
endmacro()
macro(shiboken_validate_python_version)
- if(PYTHON_VERSION_MAJOR EQUAL "3" AND PYTHON_VERSION_MINOR LESS "7")
+ if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7")
message(FATAL_ERROR
"Shiboken requires Python 3.7+.")
endif()
if (SHIBOKEN_COMPUTE_INCLUDES_IS_CALLED_FROM_EXPORT)
#TODO target_include_directories works on imported targets only starting with v3.11.0.
set_property(TARGET Shiboken6::libshiboken
- APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS})
+ APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Python_INCLUDE_DIRS})
else()
target_include_directories(libshiboken
- PUBLIC $<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>)
+ PUBLIC $<BUILD_INTERFACE:${Python_INCLUDE_DIRS}>)
endif()
- set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS}")
+ set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}")
set_property(GLOBAL PROPERTY shiboken_python_include_dirs "${SHIBOKEN_PYTHON_INCLUDE_DIRS}")
set(SHIBOKEN_PYTHON_LIBRARIES "")
endif()
- if(CMAKE_BUILD_TYPE STREQUAL "Debug")
- if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
- set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_DEBUG_LIBRARIES})
- endif()
- endif()
-
- if(CMAKE_BUILD_TYPE STREQUAL "Release")
- if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
- set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_LIBRARIES})
- endif()
+ if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
+ set(SHIBOKEN_PYTHON_LIBRARIES ${Python_LIBRARIES})
endif()
# If the resulting variable
endmacro()
function(shiboken_check_if_built_and_target_python_are_compatible)
- if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL PYTHON_VERSION_MAJOR)
+ if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL Python_VERSION_MAJOR)
message(FATAL_ERROR "The detected Python major version is not \
compatible with the Python major version which was used when Shiboken was built.
Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \
-Detected: '${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}'")
+Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'")
else()
if(NOT SHIBOKEN_PYTHON_LIMITED_API
- AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL PYTHON_VERSION_MINOR)
+ AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL Python_VERSION_MINOR)
message(FATAL_ERROR
"The detected Python minor version is not compatible with the Python minor \
version which was used when Shiboken was built. Consider building shiboken with \
FORCE_LIMITED_API set to '1', so that only the Python major version matters.
Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \
-Detected: '${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}'")
+Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'")
endif()
endif()
endfunction()
# tool_name should be a unique tool name, preferably without spaces.
# Returns the wrapper path in path_out_var.
#
-# Currently adds the Qt lib dir and libclang to PATH.
+# Currently adds the Qt lib dir and libclang to PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH.
# Meant to be used as the first argument to add_custom_command's COMMAND option.
# TODO: Remove tool_name as the tool_name for this function is always shiboken.
function(shiboken_get_tool_shell_wrapper tool_name path_out_var)
-
- # Make sure that for cross building, the host shiboken_wrapper.sh tool is used instead of target
- # shiboken_wrapper.sh when calling shiboken. This wrapper script resolves the dependency to Qt
- # libraries.
- if((SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD) AND
- (CMAKE_HOST_UNIX AND NOT CMAKE_HOST_APPLE))
- set(host_tool_wrapper_path
- "${QFP_SHIBOKEN_HOST_PATH}/../build/shiboken6/.qfp/bin/shiboken_wrapper.sh")
-
- if(EXISTS "${host_tool_wrapper_path}")
- set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_shiboken_path"
- "${host_tool_wrapper_path}")
- set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_shiboken_created" TRUE)
- else()
- message(FATAL_ERROR "${host_tool_wrapper_path} does not exist")
- endif()
- endif()
-
# Generate the wrapper only once during the execution of CMake.
get_property(is_called GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created")
set(path_dirs_native "")
if(CMAKE_HOST_WIN32)
+ set(wrapper_script_extension ".bat")
+ else()
+ set(wrapper_script_extension ".sh")
+ endif()
+
+ # Try to get original host shiboken paths from exported target properties.
+ shiboken_get_host_tool_wrapper_properties(orig_qt_library_dir_absolute orig_libclang_lib_dir)
+
+ # Get path to the Qt bin/lib dir depending on the platform and developer input.
+ # Prefer values given on the command line, then the original host path if it exists, otherwise
+ # try to use the Qt install prefix and libclang env vars.
+ #
+ # Note that in a cross-compiling case, using the Qt install prefix is very likely
+ # wrong, because you want to use the location of the host Qt, not the target Qt. Same for
+ # libclang. Unfortunately we currently don't provide a host Qt and host libclang option via
+ # setup.py, so the manual cmake vars will have to suffice.
+ if(SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}")
+ set(qt_library_dir_absolute "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}")
+ elseif(orig_qt_library_dir_absolute AND EXISTS "${orig_qt_library_dir_absolute}")
+ set(qt_library_dir_absolute "${orig_qt_library_dir_absolute}")
+ elseif(CMAKE_HOST_WIN32)
# in Windows the Qt dll are store `bin` in directory
set(qt_library_dir ${QT6_INSTALL_BINS})
- set(wrapper_script_extension ".bat")
else()
# in Unix the .so are stored in `lib` directory
set(qt_library_dir ${QT6_INSTALL_LIBS})
- set(wrapper_script_extension ".sh")
endif()
# Assert that Qt is already found.
- if(NOT QT6_INSTALL_PREFIX OR NOT qt_library_dir)
+ if((QT6_INSTALL_PREFIX AND qt_library_dir) OR orig_qt_library_dir_absolute)
+ else()
message(FATAL_ERROR "Qt should have been found already by now.")
endif()
- # Get path to the Qt bin/lib dir depending on the platform
- list(APPEND path_dirs "${QT6_INSTALL_PREFIX}/${qt_library_dir}")
+ if(NOT qt_library_dir_absolute)
+ set(qt_library_dir_absolute "${QT6_INSTALL_PREFIX}/${qt_library_dir}")
+ endif()
+ list(APPEND path_dirs "${qt_library_dir_absolute}")
+
+ # Get libclang lib dir path.
+ # Prefer values given on the command line, then the original host path if it exists.
+ if(SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}")
+ set(libclang_lib_dir "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}")
+ elseif(orig_libclang_lib_dir AND EXISTS "${orig_libclang_lib_dir}")
+ set(libclang_lib_dir "${orig_libclang_lib_dir}")
+ else()
+ # find libclang
+ find_libclang()
+ endif()
- # find libclang
- find_libclang()
if(libclang_lib_dir)
list(APPEND path_dirs "${libclang_lib_dir}")
endif()
set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_path" "${wrapper_path}")
set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created" TRUE)
+ # Save original host paths for future cross-builds.
+ shiboken_save_host_tool_wrapper_properties("${qt_library_dir_absolute}" "${libclang_lib_dir}")
+
# give execute permission to run the file
if(CMAKE_HOST_UNIX)
execute_process(COMMAND chmod +x ${wrapper_path})
set(${path_out_var} "${wrapper_path}" PARENT_SCOPE)
endfunction()
+# Retrieve the original host shiboken runtime dependency paths from the installed (namespaced)
+# shiboken generator target.
+function(shiboken_get_host_tool_wrapper_properties out_qt_library_dir out_libclang_lib_dir)
+ if(TARGET Shiboken6::shiboken6)
+ get_target_property(qt_library_dir Shiboken6::shiboken6 _shiboken_original_qt_lib_dir)
+ if(NOT qt_library_dir)
+ set(qt_library_dir "")
+ endif()
+ get_target_property(libclang_lib_dir Shiboken6::shiboken6
+ _shiboken_original_libclang_lib_dir)
+ if(NOT libclang_lib_dir)
+ set(libclang_lib_dir "")
+ endif()
+ endif()
+
+ set(${out_qt_library_dir} "${qt_library_dir}" PARENT_SCOPE)
+ set(${out_libclang_lib_dir} "${libclang_lib_dir}" PARENT_SCOPE)
+endfunction()
+
+# Save original host shiboken runtime dependency paths as target properties, so they can be used
+# when generating the wrapper file for cross-builds.
+# Should only be done when shiboken is being built (aka it's a non-imported target).
+function(shiboken_save_host_tool_wrapper_properties qt_library_dir libclang_lib_dir)
+ if(TARGET shiboken6)
+ get_target_property(is_imported shiboken6 IMPORTED)
+ if(is_imported)
+ return()
+ endif()
+
+ set_target_properties(shiboken6 PROPERTIES
+ _shiboken_original_qt_lib_dir "${qt_library_dir}")
+ set_property(TARGET shiboken6 APPEND PROPERTY
+ EXPORT_PROPERTIES _shiboken_original_qt_lib_dir)
+ if(libclang_lib_dir)
+ set_target_properties(shiboken6 PROPERTIES
+ _shiboken_original_libclang_lib_dir "${libclang_lib_dir}")
+ set_property(TARGET shiboken6 APPEND PROPERTY
+ EXPORT_PROPERTIES _shiboken_original_libclang_lib_dir)
+ endif()
+ endif()
+endfunction()
+
# Returns the platform-specific relative rpath base token, if it's supported.
# If it's not supported, returns the string NO_KNOWN_RPATH_REL_BASE.
function(get_rpath_base_token out_var)
include(ShibokenHelpers)
-option(USE_PYTHON_VERSION "Use specific python version to build shiboken6." "")
option(DISABLE_DOCSTRINGS "Disable documentation extraction." FALSE)
shiboken_internal_disable_pkg_config_if_needed()
compute_config_py_values(shiboken6_VERSION)
## For debugging the PYTHON* variables
-message(STATUS "PYTHONLIBS_FOUND: " ${PYTHONLIBS_FOUND})
-message(STATUS "PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES})
-message(STATUS "PYTHON_INCLUDE_DIRS: " ${PYTHON_INCLUDE_DIRS})
-message(STATUS "PYTHON_DEBUG_LIBRARIES: " ${PYTHON_DEBUG_LIBRARIES})
-message(STATUS "PYTHONINTERP_FOUND: " ${PYTHONINTERP_FOUND})
-message(STATUS "PYTHON_EXECUTABLE: " ${PYTHON_EXECUTABLE})
-message(STATUS "PYTHON_VERSION: " ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH})
+message(STATUS "Python_Development_FOUND: " ${Python_Development_FOUND})
+message(STATUS "Python_LIBRARIES: " ${Python_LIBRARIES})
+message(STATUS "Python_INCLUDE_DIRS: " ${Python_INCLUDE_DIRS})
+message(STATUS "Python_Interpreter_FOUND: " ${Python_Interpreter_FOUND})
+message(STATUS "Python_EXECUTABLE: " ${Python_EXECUTABLE})
+message(STATUS "Python_VERSION: " ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.${Python_VERSION_PATCH})
if(NOT PYTHON_EXTENSION_SUFFIX)
get_python_extension_suffix()
# Detect if the python libs were compiled in debug mode
# On Linux distros there is no standard way to check that.
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+ COMMAND ${Python_EXECUTABLE} -c "if True:
import sys
import sysconfig
config_py_debug = sysconfig.get_config_var('Py_DEBUG')
set(PYTHON_WITH_COUNT_ALLOCS 0)
else()
execute_process(
- COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+ COMMAND ${Python_EXECUTABLE} -c "if True:
count_allocs = False
import sys
try:
# This is the version of Python against which Shiboken was built. Not necessarily the version
# against which a downstream project is built (e.g. PySide6).
-set(SHIBOKEN_PYTHON_VERSION_MAJOR "@PYTHON_VERSION_MAJOR@")
-set(SHIBOKEN_PYTHON_VERSION_MINOR "@PYTHON_VERSION_MINOR@")
-set(SHIBOKEN_PYTHON_VERSION_PATCH "@PYTHON_VERSION_PATCH@")
+set(SHIBOKEN_PYTHON_VERSION_MAJOR "@Python_VERSION_MAJOR@")
+set(SHIBOKEN_PYTHON_VERSION_MINOR "@Python_VERSION_MINOR@")
+set(SHIBOKEN_PYTHON_VERSION_PATCH "@Python_VERSION_PATCH@")
set(SHIBOKEN_PYTHON_LIMITED_API "@PYTHON_LIMITED_API@")
# Import targets and call variable set up functions only when using an installed shiboken config
include("${CMAKE_CURRENT_LIST_DIR}/ShibokenHelpers.cmake")
# Compute the python include and libraries path if needed (aka not part of super project build).
- shiboken_find_required_python(@PYTHON_VERSION_MAJOR@)
+ shiboken_find_required_python(@Python_VERSION_MAJOR@)
shiboken_check_if_built_and_target_python_are_compatible()
shiboken_check_if_limited_api()
shiboken_compute_python_includes(IS_CALLED_FROM_EXPORT)
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
includedir=@CMAKE_INSTALL_PREFIX@/include/shiboken6
-python_interpreter=@PYTHON_EXECUTABLE@
-python_include_dir=@PYTHON_INCLUDE_DIRS@
+python_interpreter=@Python_EXECUTABLE@
+python_include_dir=@Python_INCLUDE_DIRS@
Name: shiboken6
Description: Support library for Python bindings created with the Shiboken6 generator.
Version: @shiboken6_VERSION@
Libs: @SHIBOKEN_PYTHON_LIBRARIES@ -L${libdir} -lshiboken6@shiboken6_SUFFIX@@PYTHON_SHARED_LIBRARY_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@
-Cflags: -I@PYTHON_INCLUDE_DIRS@ -I${includedir}/@shiboken6_SUFFIX@@SBK_PKG_CONFIG_PY_DEBUG_DEFINITION@
+Cflags: -I@Python_INCLUDE_DIRS@ -I${includedir}/@shiboken6_SUFFIX@@SBK_PKG_CONFIG_PY_DEBUG_DEFINITION@
if(SHIBOKEN_IS_CROSS_BUILD)
set(python_executable "${QFP_PYTHON_HOST_PATH}")
else()
- set(python_executable "${PYTHON_EXECUTABLE}")
+ set(python_executable "${Python_EXECUTABLE}")
endif()
if(NOT python_executable OR NOT EXISTS "${python_executable}")
message(FATAL_ERROR "No python executable found to build documentation.")
# General information about the project.
project = u'Shiboken'
-copyright = u'2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
+copyright = u'2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
``--inheritance-file=<file>``
Generate a JSON file containing the class inheritance.
+``--disable-inheritance-diagram``
+ Disable the generation of the inheritance diagram.
+
.. _project-file:
********************
generate-using="yes | no"
package="..."
since="..."
+ extends = "..."
+ files = "..."
revision="..." />
</typesystem>
The **revision** attribute can be used to specify a revision for each type, easing the
production of ABI compatible bindings.
+The *optional* **extends** attribute specifies the module name where the given
+namespace first occurs in case of a namespace spanning several modules. For
+example, in Qt, the namespace ``Qt`` first occurs in the ``QtCore`` module and
+is further populated in the ``QtGui`` module. ``QtGui.Qt`` will then be
+generated extending ``QtCore.Qt`` if **extends** is specified.
+
+The *optional* **file** attribute specifies a regular expression matching the
+include files whose contents are to be associated with the current module in
+case of a namespace spanning several modules.
+
.. _enum-type:
enum-type
set(CMAKE_AUTOMOC ON)
-if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND PYTHONINTERP_FOUND))
+if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND Python_Interpreter_FOUND))
message(WARNING "Some dependencies were not found: shiboken6 generator compilation disabled!")
return()
endif()
if (type.isNativePointer())
return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName());
if (type.isPointer())
- return DefaultValue(DefaultValue::Pointer, u"::"_s + type.typeEntry()->qualifiedCppName());
+ return DefaultValue(DefaultValue::Pointer, getFullTypeName(type.typeEntry()));
if (type.typeEntry()->isSmartPointer())
return minimalConstructor(api, type.typeEntry());
if (const auto nullValue = enumEntry->nullValue())
return DefaultValue(DefaultValue::Enum, nullValue->name());
return DefaultValue(DefaultValue::Custom,
- u"static_cast< ::"_s + type->qualifiedCppName()
- + u">(0)"_s);
+ "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1);
}
if (type->isFlags()) {
#include <abstractmetafield.h>
#include <abstractmetafunction.h>
#include <abstractmetalang.h>
+#include "abstractmetalang_helpers.h"
#include <fileout.h>
#include <messages.h>
#include <modifications.h>
#include <functiontypeentry.h>
#include <enumtypeentry.h>
#include <complextypeentry.h>
+#include <flagstypeentry.h>
+#include <primitivetypeentry.h>
#include <qtdocparser.h>
#include <doxygenparser.h>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
+#include <QtCore/QSet>
#include <algorithm>
#include <limits>
QString additionalDocumentationList;
QString inheritanceFile;
bool doxygen = false;
+ bool inheritanceDiagram = true;
};
struct GeneratorDocumentation
s << '\n';
}
+static void writeInheritanceList(TextStream &s, const AbstractMetaClassCList& classes,
+ const char *label)
+{
+ s << "**" << label << ":** ";
+ for (qsizetype i = 0, size = classes.size(); i < size; ++i) {
+ if (i > 0)
+ s << ", ";
+ s << ":ref:`" << classes.at(i)->name() << '`';
+ }
+ s << "\n\n";
+}
+
static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &metaClass,
const AbstractMetaClassCList& allClasses)
{
res << c;
}
- if (res.isEmpty())
- return;
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherited by");
+}
+
+static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+ AbstractMetaClassCList res;
- s << "**Inherited by:** ";
- QStringList classes;
- for (const auto &c : std::as_const(res))
- classes << u":ref:`"_s + c->name() + u'`';
- s << classes.join(u", "_s) << "\n\n";
+ recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) {
+ if (c.get() != metaClass.get())
+ res.append(c);
+ return false;
+ });
+
+ if (!res.isEmpty())
+ writeInheritanceList(s, res, "Inherits from");
}
void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
if (documentation.hasBrief())
writeFormattedBriefText(s, documentation, metaClass);
- s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
- << " :parts: 2\n\n";
- // TODO: This would be a parameter in the future...
-
+ if (!metaClass->baseClasses().isEmpty()) {
+ if (m_options.inheritanceDiagram) {
+ s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
+ << " :parts: 2\n\n";
+ } else {
+ writeInheritedFromList(s, metaClass);
+ }
+ }
writeInheritedByList(s, metaClass, api().classes());
return funcName + formatArgs(func);
}
+static QString inline toRef(const QString &t)
+{
+ return ":any:`"_L1 + t + u'`';
+}
+
QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
const AbstractMetaClassCPtr &cppClass,
bool createRef) const
static const QStringList nativeTypes =
{boolT(), floatT(), intT(), pyObjectT(), pyStrT()};
- const QString name = type.name();
+ QString name = type.name();
if (nativeTypes.contains(name))
return name;
- static const QMap<QString, QString> typeMap = {
+ if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) {
+ const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name();
+ if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
+ || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
+ return intT();
+ }
+ if (AbstractMetaType::cppFloatTypes().contains(basicName))
+ return floatT();
+ }
+
+ static const QSet<QString> stringTypes = {
+ u"uchar"_s, u"std::string"_s, u"std::wstring"_s,
+ u"std::stringview"_s, u"std::wstringview"_s,
+ qStringT(), u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
+ };
+ if (stringTypes.contains(name))
+ return pyStrT();
+
+ static const QHash<QString, QString> typeMap = {
{ cPyObjectT(), pyObjectT() },
- { qStringT(), pyStrT() },
- { u"uchar"_s, pyStrT() },
{ u"QStringList"_s, u"list of strings"_s },
- { qVariantT(), pyObjectT() },
- { u"quint32"_s, intT() },
- { u"uint32_t"_s, intT() },
- { u"quint64"_s, intT() },
- { u"qint64"_s, intT() },
- { u"size_t"_s, intT() },
- { u"int64_t"_s, intT() },
- { u"qreal"_s, floatT() }
+ { qVariantT(), pyObjectT() }
};
- const auto found = typeMap.find(name);
- if (found != typeMap.end())
+ const auto found = typeMap.constFind(name);
+ if (found != typeMap.cend())
return found.value();
- QString strType;
- if (type.isConstant() && name == u"char" && type.indirections() == 1) {
- strType = u"str"_s;
- } else if (name.startsWith(unsignedShortT())) {
- strType = intT();
- } else if (name.startsWith(unsignedT())) { // uint and ulong
- strType = intT();
- } else if (type.isContainer()) {
+ if (type.isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry());
+ auto enumName = fte->originator()->targetLangName();
+ return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
+ }
+
+ if (type.isConstant() && name == "char"_L1 && type.indirections() == 1)
+ return "str"_L1;
+
+ if (type.isContainer()) {
QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference);
strType.remove(u'*');
strType.remove(u'>');
strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.")
.arg(types[0], types[1]);
}
- } else {
- auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry());
- strType = k ? k->fullName() : type.name();
- if (createRef) {
- strType.prepend(u":any:`"_s);
- strType.append(u'`');
- }
+ return strType;
}
- return strType;
+
+ if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()))
+ return createRef ? toRef(k->fullName()) : k->fullName();
+
+ return createRef ? toRef(name) : name;
}
QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc)
u"List of additional XML files to be converted to .rst files\n"
"(for example, tutorials)."_s},
{u"inheritance-file=<file>"_s,
- u"Generate a JSON file containing the class inheritance."_s}
+ u"Generate a JSON file containing the class inheritance."_s},
+ {u"disable-inheritance-diagram"_s,
+ u"Disable the generation of the inheritance diagram."_s}
};
}
public:
explicit QtDocGeneratorOptionsParser(DocGeneratorOptions *o) : m_options(o) {}
+ bool handleBoolOption(const QString &key, OptionSource source) override;
bool handleOption(const QString &key, const QString &value, OptionSource source) override;
private:
DocGeneratorOptions *m_options;
};
+bool QtDocGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource)
+{
+ if (key == "disable-inheritance-diagram"_L1) {
+ m_options->inheritanceDiagram = false;
+ return true;
+ }
+ return false;
+}
+
bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value,
OptionSource source)
{
|| m_lastTagName == u"dots" || m_lastTagName == u"codeline";
if (consecutiveSnippet) {
m_output.flush();
- m_output.string()->chop(2);
+ m_output.string()->chop(1); // Strip newline from previous snippet
}
QString location = reader.attributes().value(u"location"_s).toString();
QString identifier = reader.attributes().value(u"identifier"_s).toString();
<< methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n";
}
+void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ if (!codeSnips.isEmpty()) {
+ try {
+ writeCodeSnips(s, codeSnips, position, language);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what()));
+ }
+ }
+}
+
bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c)
{
return !c->typeEntry()->hashFunction().isEmpty()
if (isProtectedEnum) {
QString typeCast;
if (metaEnum->enclosingClass())
- typeCast += u"::"_s + metaEnum->enclosingClass()->qualifiedCppName();
+ typeCast += getFullTypeName(metaEnum->enclosingClass());
typeCast += u"::"_s + metaEnum->name();
s << '(' << typeCast << ')';
}
<< "\", \"" << v.name() << "\");\nbreak;\n" << outdent;
}
if (deprecatedValues.size() < metaEnum.values().size())
- c << "default:\n" << indent << "break;\n" << outdent << "}\n";
+ c << "default:\n" << indent << "break;\n" << outdent;
+ c << "}\n";
}
void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const
&& cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper);
Q_ASSERT(!useWrapperClass || context.useWrapper());
const QString className = useWrapperClass
- ? context.wrapperName()
- : (u"::"_s + metaClass->qualifiedCppName());
+ ? context.wrapperName() : getFullTypeName(metaClass);
writeInvalidPyObjectCheck(s, u"self"_s, errorReturn);
{
QString prettyCode = code;
- processCodeSnip(prettyCode);
+ const QString funcName = cppToPythonFunctionName(sourceTypeName, targetTypeName);
+ processCodeSnip(prettyCode, funcName);
- s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName)
+ s << "static PyObject *" << funcName
<< "(const void *cppIn)\n{\n" << indent << prettyCode
<< ensureEndl << outdent << "}\n";
}
code.replace(u"%INTYPE_"_s + QString::number(i), typeName);
}
replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true);
- processCodeSnip(code);
+ processCodeSnip(code, containerType.typeEntry()->qualifiedCppName());
writeCppToPythonFunction(s, code, fixedCppTypeName(containerType),
containerNativeToTargetTypeName(cte));
}
const QString &targetTypeName) const
{
QString prettyCode = code;
- processCodeSnip(prettyCode);
- s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName)
+ const QString funcName = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+ processCodeSnip(prettyCode, funcName);
+ s << "static void " << funcName
<< "(PyObject *pyIn, void *cppOut)\n{\n" << indent << prettyCode
<< ensureEndl << outdent << "}\n";
}
+ cpythonTypeNameExt(toNative.sourceType()) + u')';
}
typeCheck.replace(u"%in"_s, u"pyIn"_s);
- processCodeSnip(typeCheck);
+ processCodeSnip(typeCheck, targetType->qualifiedCppName());
writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
}
if (!func->isStatic()) {
const bool directInheritance = context.metaClass() == ownerClass;
mc << (directInheritance ? "static_cast" : "reinterpret_cast")
- << "<::" << wrapperName(ownerClass) << " *>(" << CPP_SELF_VAR << ")->";
+ << '<' << wrapperName(ownerClass) << " *>("
+ << CPP_SELF_VAR << ")->";
}
if (!func->isAbstract())
s << "else ";
s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry())
<< ")\n" << indent
- << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n"
+ << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n"
<< outdent;
firstClass = false;
}
if (usePySideExtensions() && isQObject(metaClass)) {
s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n"
<< "PySide::initDynamicMetaObject(pyType, &::"
- << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof(";
- if (shouldGenerateCppWrapper(metaClass))
- s << wrapperName(metaClass);
- else
- s << "::" << metaClass->qualifiedCppName();
- s << "));\n";
+ << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("
+ << (shouldGenerateCppWrapper(metaClass)
+ ? wrapperName(metaClass) : getFullTypeName(metaClass))
+ << "));\n";
}
s << outdent << "}\n";
if (ancestor->isPolymorphic()) {
s << "if (instanceType == Shiboken::SbkType< ::"
<< ancestor->qualifiedCppName() << " >())\n" << indent
- << "return dynamic_cast< ::" << metaClass->qualifiedCppName()
- << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName()
+ << "return dynamic_cast< " << getFullTypeName(metaClass)
+ << " *>(reinterpret_cast< "<< getFullTypeName(ancestor)
<< " *>(cptr));\n" << outdent;
} else {
qCWarning(lcShiboken).noquote().nospace()
const CodeSnipList snips = moduleEntry->codeSnips();
// module inject-code native/beginning
- if (!snips.isEmpty())
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
// cleanup staticMetaObject attribute
if (usePySideExtensions()) {
<< indent << "return " << globalModuleVar << ";\n" << outdent;
// module inject-code target/beginning
- if (!snips.isEmpty())
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode);
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::TargetLangCode);
for (const QString &requiredModule : requiredModules) {
s << "{\n" << indent
<< outdent << "}\n";
// module inject-code target/end
- if (!snips.isEmpty())
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
// module inject-code native/end
- if (!snips.isEmpty())
- writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
+ writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
if (usePySideExtensions()) {
for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums))
if (!metaEnum.isAnonymous()) {
ConfigurableScope configScope(s, metaEnum.typeEntry());
- s << "qRegisterMetaType< ::" << metaEnum.typeEntry()->qualifiedCppName()
+ s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry())
<< " >(\"" << metaEnum.name() << "\");\n";
}
static void writePyMethodDefs(TextStream &s, const QString &className,
const QString &methodsDefinitions);
+ void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const;
+
static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass)
{ return boolCast(metaClass).has_value(); }
#include <abstractmetalang_helpers.h>
#include <codesnip.h>
#include <clangparser/compilersupport.h>
+#include <exception.h>
#include <typedatabase.h>
#include <reporthandler.h>
#include <textstream.h>
#include "containertypeentry.h"
#include "enumtypeentry.h"
#include "flagstypeentry.h"
+#include <messages.h>
#include "namespacetypeentry.h"
#include "primitivetypeentry.h"
#include "typedefentry.h"
StringStream macrosStream(TextStream::Language::Cpp);
const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
- if (!snips.isEmpty()) {
- writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
- TypeSystem::TargetLangCode);
- }
+ writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::TargetLangCode);
macrosStream << "// Type indices\nenum : int {\n";
auto classList = api().classes();
void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass)
{
- s << "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() "
- << "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
+ s << "template<> inline PyTypeObject *SbkType< "
+ << getFullTypeName(cppClass) << " >() "
+ << "{ return reinterpret_cast<PyTypeObject *>("
+ << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
}
void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType)
s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() "
<< "{ return " << cpythonTypeNameExt(metaType) << "; }\n";
}
+
+void HeaderGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ if (!codeSnips.isEmpty()) {
+ try {
+ writeCodeSnips(s, codeSnips, position, language);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError("module header of "_L1 + moduleName(), e.what()));
+ }
+ }
+}
#include "shibokengenerator.h"
#include "include.h"
+#include "modifications_typedefs.h"
#include <QtCore/QSet>
void writeWrapperClass(TextStream &s, const QString &wrapperName, const GeneratorContext &classContext) const;
void writeInheritedWrapperClassDeclaration(TextStream &s,
const GeneratorContext &classContext) const;
+ void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const;
AbstractMetaClassCList m_alternateTemplateIndexes;
};
{
if (!type->isWrapperType())
return QString();
- return u"reinterpret_cast< ::"_s + type->qualifiedCppName()
+ return u"reinterpret_cast< "_s + getFullTypeName(type)
+ u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type)
+ u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
}
code.replace(u"%TYPE"_s, className);
code.replace(u"%CPPTYPE"_s, metaClass->name());
- processCodeSnip(code);
+ processCodeSnip(code, context.effectiveClassName());
}
void ShibokenGenerator::processCodeSnip(QString &code) const
replaceTypeCheckTypeSystemVariable(code);
}
+void ShibokenGenerator::processCodeSnip(QString &code, const QString &context) const
+{
+ try {
+ processCodeSnip(code);
+ } catch (const std::exception &e) {
+ throw Exception(msgSnippetError(context, e.what()));
+ }
+}
+
ShibokenGenerator::ArgumentVarReplacementList
ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunctionCPtr &func,
bool usePyArgs, TypeSystem::Language language,
replaceTemplateVariables(code, func);
- processCodeSnip(code);
+ processCodeSnip(code, func->classQualifiedSignature());
s << "// Begin code injection\n" << code << "// End of code injection\n\n";
}
struct IncludeGroup;
struct ShibokenGeneratorOptions;
-QT_FORWARD_DECLARE_CLASS(TextStream)
+class TextStream;
// Function to be used for implementing nb_bool
struct BoolCastFunction
/// Replaces variables for the user's custom code at global or class level.
void processCodeSnip(QString &code) const;
+ void processCodeSnip(QString &code, const QString &context) const;
void processClassCodeSnip(QString &code, const GeneratorContext &context) const;
/**
set(host_python_path "${QFP_PYTHON_HOST_PATH}")
set(use_pyc_in_embedding FALSE)
else()
- set(host_python_path "${PYTHON_EXECUTABLE}")
+ set(host_python_path "${Python_EXECUTABLE}")
if(PYTHON_LIMITED_API)
set(use_pyc_in_embedding FALSE)
else()
auto pyObj = reinterpret_cast<PyObject *>(sbkType);
PyObject_GC_UnTrack(pyObj);
-#ifndef Py_LIMITED_API
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
# if PY_VERSION_HEX >= 0x030A0000
Py_TRASHCAN_BEGIN(pyObj, 1);
# else
Shiboken::Conversions::deleteConverter(sotp->converter);
PepType_SOTP_delete(sbkType);
}
-#ifndef Py_LIMITED_API
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
# if PY_VERSION_HEX >= 0x030A0000
Py_TRASHCAN_END;
# else
return result;
}
-#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000
+#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
static int _getPyVerbose()
{
PyConfig config;
{
#ifdef Py_LIMITED_API
return Pep_GetVerboseFlag();
-#elif PY_VERSION_HEX >= 0x030A0000
+#elif PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
static const int result = _getPyVerbose();
return result;
#else
#endif // Py_LIMITED_API
+#if defined(Py_LIMITED_API) || defined(PYPY_VERSION)
+PyObject *PepFunction_GetDefaults(PyObject *function)
+{
+ auto *ob_ret = PyObject_GetAttrString(function, "__defaults__");
+ Py_XDECREF(ob_ret); // returns borrowed ref
+ return ob_ret != Py_None ? ob_ret : nullptr;
+}
+
+#endif // defined(Py_LIMITED_API) || defined(PYPY_VERSION)
+
/*****************************************************************************
*
* Support for datetime.h
# define PepCode_GET_FLAGS(o) ((o)->co_flags)
# define PepCode_GET_ARGCOUNT(o) ((o)->co_argcount)
# define PepCode_Check PyCode_Check
+
+# ifdef PYPY_VERSION
+
+LIBSHIBOKEN_API PyObject *PepFunction_GetDefaults(PyObject *function);
+
+# else
+# define PepFunction_GetDefaults PyFunction_GetDefaults
+# endif
#endif
/*****************************************************************************
#define SHIBOKEN_MICRO_VERSION @shiboken_MICRO_VERSION@
#define SHIBOKEN_RELEASE_LEVEL "final"
#define SHIBOKEN_SERIAL 0
-#define PYTHON_VERSION_MAJOR @PYTHON_VERSION_MAJOR@
-#define PYTHON_VERSION_MINOR @PYTHON_VERSION_MINOR@
-#define PYTHON_VERSION_PATCH @PYTHON_VERSION_PATCH@
+#define PYTHON_VERSION_MAJOR @Python_VERSION_MAJOR@
+#define PYTHON_VERSION_MINOR @Python_VERSION_MINOR@
+#define PYTHON_VERSION_PATCH @Python_VERSION_PATCH@
#endif
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# flake8: noqa E:203
+
"""
mapping.py
from typing import TypeVar, Generic
from _imp import is_builtin
+
class ellipsis(object):
def __repr__(self):
return "..."
+
ellipsis = ellipsis()
Point = typing.Tuple[int, int]
Variant = typing.Any
MultiMap = typing.DefaultDict[str, typing.List[str]]
# ulong_max is only 32 bit on windows.
-ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff
+ulong_max = 2 * sys.maxsize + 1 if len(struct.pack("L", 1)) != 4 else 0xffffffff
ushort_max = 0xffff
GL_COLOR_BUFFER_BIT = 0x00004000
text = self if self.endswith(")") else self + "()"
return eval(text, namespace)
+
USE_PEP563 = False
# Note: we cannot know if this feature has been imported.
# Otherwise it would be "sys.version_info[:2] >= (3, 7)".
class Virtual(_NotCalled):
pass
+
# Other types I simply could not find.
class Missing(_NotCalled):
# The string must be quoted, because the object does not exist.
class Invalid(_NotCalled):
pass
+
# Helper types
class Default(_NotCalled):
pass
class Instance(_NotCalled):
pass
+
# Parameterized primitive variables
class _Parameterized(object):
def __init__(self, type):
def __repr__(self):
return f"{type(self).__name__}({self.type.__name__})"
+
# Mark the primitive variables to be moved into the result.
class ResultVariable(_Parameterized):
pass
+
# Mark the primitive variables to become Sequence, Iterable or List
# (decided in the parser).
class ArrayLikeVariable(_Parameterized):
pass
+
StringList = ArrayLikeVariable(str)
mod_name = mod.__name__
raise ImportError(f"Module '{mod_name}' is not a binary module!")
+
update_mapping = Reloader().update
type_map = {}
namespace = globals() # our module's __dict__
"PyCallable": typing.Callable,
"PyObject": object,
"PyObject*": object,
- "PyArrayObject": ArrayLikeVariable, # numpy
+ "PyArrayObject": ArrayLikeVariable, # numpy
"PyPathLike": typing.Union[str, bytes, os.PathLike],
- "PySequence": typing.Iterable, # important for numpy
+ "PySequence": typing.Iterable, # important for numpy
"PyTypeObject": type,
"QChar": str,
"QHash": typing.Dict,
"ulong": int,
"ULONG_MAX": ulong_max,
"UINT64_MAX": 0xffffffff,
- "unsigned char": int, # 5.9
+ "unsigned char": int, # 5.9
"unsigned char*": str,
"unsigned int": int,
- "unsigned long int": int, # 5.6, RHEL 6.6
+ "unsigned long int": int, # 5.6, RHEL 6.6
"unsigned long long": int,
"unsigned long": int,
- "unsigned short int": int, # 5.6, RHEL 6.6
+ "unsigned short int": int, # 5.6, RHEL 6.6
"unsigned short": int,
"ushort": int,
- "void": int, # be more specific?
+ "void": int, # be more specific?
"WId": WId,
"zero(bytes)": b"",
"zero(Char)": 0,
"numpy.ndarray": typing.List[typing.Any],
"std.array[int, 4]": typing.List[int],
"std.array[float, 4]": typing.List[float]
- })
+})
type_map.update({
# Handling variables declared as array:
"array long long*" : ArrayLikeVariable(int),
"array long*" : ArrayLikeVariable(int),
"array short*" : ArrayLikeVariable(int),
- "array signed char*" : bytes,
- "array unsigned char*" : bytes,
+ "array signed char*" : typing.Union[bytes, bytearray, memoryview],
+ "array unsigned char*" : typing.Union[bytes, bytearray, memoryview],
"array unsigned int*" : ArrayLikeVariable(int),
"array unsigned short*" : ArrayLikeVariable(int),
# PYSIDE-1646: New macOS primitive types
"array int32_t*" : ArrayLikeVariable(int),
"array uint32_t*" : ArrayLikeVariable(int),
"array intptr_t*" : ArrayLikeVariable(int),
- })
+})
type_map.update({
# Special cases:
- "char*" : bytes,
- "QChar*" : bytes,
+ "char*" : typing.Union[bytes, bytearray, memoryview],
+ "QChar*" : typing.Union[bytes, bytearray, memoryview],
"quint32*" : int, # only for QRandomGenerator
"quint8*" : bytearray, # only for QCborStreamReader and QCborValue
- "uchar*" : bytes,
- "unsigned char*": bytes,
- })
+ "uchar*" : typing.Union[bytes, bytearray, memoryview],
+ "unsigned char*": typing.Union[bytes, bytearray, memoryview],
+})
type_map.update({
# Handling variables that are returned, eventually as Tuples:
"uint*" : ResultVariable(int),
"unsigned int*" : ResultVariable(int),
"QStringList*" : ResultVariable(StringList),
- })
+})
type_map.update({
"[typing.Any]" : [typing.Any],
"[typing.Any,typing.Any]" : [typing.Any, typing.Any],
"None" : None,
- })
+})
# PYSIDE-1328: We need to handle "self" explicitly.
type_map.update({
"self" : "self",
"cls" : "cls",
- })
+})
# PYSIDE-1538: We need to treat "std::optional" accordingly.
type_map.update({
"std.optional": typing.Optional,
})
+
# The Shiboken Part
def init_Shiboken():
type_map.update({
})
return locals()
+
def init_minimal():
type_map.update({
"MinBool": bool,
"const char*": str,
"Complex": complex,
"double": float,
- "ByteArray&": bytes,
+ "ByteArray&": typing.Union[bytes, bytearray, memoryview],
"Foo.HANDLE": int,
"HANDLE": int,
"Null": None,
"OddBool": bool,
"PStr": str,
"PyDate": datetime.date,
- "PyBuffer": bytes,
+ "PyBuffer": typing.Union[bytes, bytearray, memoryview],
"sample.bool": bool,
"sample.char": int,
"sample.double": float,
# This missing type should be defined in module smart. We cannot set it to Missing()
# because it is a container type. Therefore, we supply a surrogate:
global SharedPtr
+
class SharedPtr(Generic[_S]):
__module__ = "smart"
smart.SharedPtr = SharedPtr
def init_PySide6_QtCore():
from PySide6.QtCore import Qt, QUrl, QDir, QKeyCombination
from PySide6.QtCore import QRect, QRectF, QSize, QPoint, QLocale, QByteArray
- from PySide6.QtCore import QMarginsF # 5.9
+ from PySide6.QtCore import QMarginsF # 5.9
from PySide6.QtCore import SignalInstance
try:
# seems to be not generated by 5.9 ATM.
"' '": " ",
"'%'": "%",
"'g'": "g",
- "4294967295UL": 4294967295, # 5.6, RHEL 6.6
+ "4294967295UL": 4294967295, # 5.6, RHEL 6.6
"CheckIndexOption.NoOption": Instance(
- "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11
+ "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11
"DescriptorType(-1)": int, # Native handle of QSocketDescriptor
"false": False,
"list of QAbstractAnimation": typing.List[PySide6.QtCore.QAbstractAnimation],
"long long": int,
"size_t": int,
- "NULL": None, # 5.6, MSVC
- "nullptr": None, # 5.9
- "PyBuffer": bytes,
+ "NULL": None, # 5.6, MSVC
+ "nullptr": None, # 5.9
+ "PyBuffer": typing.Union[bytes, bytearray, memoryview],
"PyByteArray": bytearray,
- "PyBytes": bytes,
+ "PyBytes": typing.Union[bytes, bytearray, memoryview],
"PyTuple": typing.Tuple,
"QDeadlineTimer(QDeadlineTimer.Forever)": Instance("PySide6.QtCore.QDeadlineTimer"),
"PySide6.QtCore.QUrl.ComponentFormattingOptions":
- PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why???
+ PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why???
"PyUnicode": typing.Text,
"QByteArrayView": QByteArray,
"Q_NULLPTR": None,
"QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"),
"QDir.SortFlags(Name | IgnoreCase)": Instance(
"QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"),
- "QEvent.Type.None" : None,
- "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok?
+ "QEvent.Type.None": None,
+ "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok?
"QGenericArgument()": ellipsis,
"QGenericArgument(0)": ellipsis,
- "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC
- "QGenericArgument(nullptr)": ellipsis, # 5.10
+ "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC
+ "QGenericArgument(nullptr)": ellipsis, # 5.10
"QGenericArgument(Q_NULLPTR)": ellipsis,
"QJsonObject": typing.Dict[str, PySide6.QtCore.QJsonValue],
- "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?!
+ "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?!
"QModelIndexList": typing.List[PySide6.QtCore.QModelIndex],
"PySideSignalInstance": SignalInstance,
"QString()": "",
"QStringList()": [],
"QStringRef": str,
"QStringRef": str,
- "Qt.HANDLE": int, # be more explicit with some constants?
+ "Qt.HANDLE": int, # be more explicit with some constants?
"QUrl.FormattingOptions(PrettyDecoded)": Instance(
"QUrl.FormattingOptions(QUrl.PrettyDecoded)"),
"QVariant()": Invalid(Variant),
- "QVariant.Type": type, # not so sure here...
- "QVariantMap": typing.Dict[str, Variant],
+ "QVariant.Type": type, # not so sure here...
"QVariantMap": typing.Dict[str, Variant],
})
try:
type_map.update({
- "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong!
+ "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong!
})
except AttributeError:
# this does not exist on 5.9 ATM.
def init_PySide6_QtGui():
- from PySide6.QtGui import QPageLayout, QPageSize # 5.12 macOS
+ from PySide6.QtGui import QPageLayout, QPageSize # 5.12 macOS
type_map.update({
"0.0f": 0.0,
"1.0f": 1.0,
"HBITMAP": int,
"HICON": int,
"HRGN": int,
- "QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp
- "QPlatformSurface*": int, # a handle
- "QVector< QTextLayout.FormatRange >()": [], # do we need more structure?
+ "QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp
+ "QPlatformSurface*": int, # a handle
+ "QVector< QTextLayout.FormatRange >()": [], # do we need more structure?
"uint32_t": int,
"uint8_t": int,
"USHRT_MAX": ushort_max,
def init_PySide6_QtWidgets():
- from PySide6.QtWidgets import QWidget, QMessageBox, QStyleOption, QStyleHintReturn, QStyleOptionComplex
- from PySide6.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem # 5.9
+ from PySide6.QtWidgets import (QWidget, QMessageBox, QStyleOption,
+ QStyleHintReturn, QStyleOptionComplex,
+ QGraphicsItem, QStyleOptionGraphicsItem)
type_map.update({
"QMessageBox.StandardButtons(Yes | No)": Instance(
"QMessageBox.StandardButtons(QMessageBox.Yes | QMessageBox.No)"),
from PySide6.QtSql import QSqlDatabase
type_map.update({
"QLatin1StringView(QSqlDatabase.defaultConnection)": QSqlDatabase.defaultConnection,
- "QVariant.Invalid": Invalid("Variant"), # not sure what I should create, here...
+ "QVariant.Invalid": Invalid("Variant"), # not sure what I should create, here...
})
return locals()
type_map.update({
"GLbitfield": int,
"GLenum": int,
- "GLfloat": float, # 5.6, MSVC 15
+ "GLfloat": float, # 5.6, MSVC 15
"GLint": int,
"GLuint": int,
})
set(test_blacklist "")
if(SHIBOKEN_IS_CROSS_BUILD)
- # PYTHON_EXECUTABLE will be empty when cross-building.
+ # Python_EXECUTABLE will be empty when cross-building.
message(WARNING
"Running tests when cross-compiling is not supported because it would require running "
"a target python interpreter which might have a different architecture than the host."
)
else()
- find_package(PythonInterp REQUIRED)
+ find_package(
+ Python
+ ${USE_PYTHON_VERSION}
+ REQUIRED
+ COMPONENTS Interpreter Development
+ )
endif()
if(NOT CTEST_TESTING_TIMEOUT)
string(REGEX MATCH "/([^/]+)(binding|module)/([^/]+)_test.py" tmp ${test_file})
set(test_name "${CMAKE_MATCH_1}_${CMAKE_MATCH_3}")
list(FIND test_blacklist ${test_name} expect_fail)
- add_test(${test_name} ${PYTHON_EXECUTABLE} ${test_file})
+ add_test(${test_name} ${Python_EXECUTABLE} ${test_file})
set_tests_properties(${test_name} PROPERTIES ENVIRONMENT "BUILD_DIR=${BUILD_DIR}")
set_tests_properties(${test_name} PROPERTIES TIMEOUT ${CTEST_TESTING_TIMEOUT})
if (${expect_fail} GREATER -1)
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from shiboken_paths import init_paths
+from shiboken_paths import init_paths # noqa: E402
init_paths()
-from shiboken6 import Shiboken
-from sample import *
+from shiboken6 import Shiboken # noqa: E402
+from sample import BlackBox, ObjectType, ObjectModel, ObjectView, Point # noqa: E402
+
class MultipleInherited (ObjectType, Point):
def __init__(self):
ObjectType.__init__(self)
Point.__init__(self)
+
class TestShiboken(unittest.TestCase):
def testIsValid(self):
self.assertTrue(Shiboken.isValid(object()))
p = ObjectType()
obj = ObjectType(p)
obj2 = ObjectType(obj)
- obj3 = ObjectType(obj)
+ obj3 = ObjectType(obj) # noqa: F841
self.assertEqual(Shiboken.dump(None), "Ordinary Python type.")
Shiboken.dump(obj)
# Don't crash even after deleting an object
Shiboken.invalidate(obj)
- Shiboken.dump(obj) # deleted
- Shiboken.dump(p) # child deleted
- Shiboken.dump(obj2) # parent deleted
+ Shiboken.dump(obj) # deleted
+ Shiboken.dump(p) # child deleted
+ Shiboken.dump(obj2) # parent deleted
def testDelete(self):
obj = ObjectType()
# we take the latest build for now.
build_history.sort()
self.history = build_history
+ self.python_version = None
self._buildno = None
if not is_ci:
# there seems to be a timing problem in RHel 7.6, so we better don't touch it
path = self.selected.build_dir
base = os.path.basename(path)
res.extend(base.split("-"))
+ # add exact Python version
+ if self.python_version:
+ res.append("py" + ".".join(map(str, self.python_version)))
# add all the python and qt subkeys
for entry in res:
parts = entry.split(".")
res.append(platform.processor())
return res
+ def set_python_version(self, version_triple):
+ self.python_version = version_triple
+
builds = BuildLog()
for idx in range(runs):
index = idx + 1
runner = TestRunner(builds.selected, project, index)
+ # For the full Python version we need to ask the TestRunner.
+ builds.set_python_version(runner.get_python_version())
print()
print(f"********* Start testing of {project} *********")
print("Config: Using", " ".join(builds.classifiers))
self._setup_clang()
self._setup()
+ def get_python_version(self):
+ """
+ Finding the exact Python version.
+ ---------------------------------
+
+ This is done by asking the interpreter, because it cannot reliably
+ be found from any file name parsing as a triple.
+
+ Note: We need to look into the CMakeCache.txt file to find out
+ what CMake has found as the Python interpreter to use.
+ This is *not* necessarily the same Python that runs this script,
+ otherwise we could use the version info directly.
+ """
+ look_python = os.path.join(self.test_dir, "CMakeCache.txt")
+ look_for = "PYTHON_EXECUTABLE:FILEPATH="
+ with open(look_python) as f:
+ for line in f:
+ if line.startswith(look_for):
+ python_exec = line.split("=")[-1].strip()
+ res = subprocess.run([python_exec, "-c",
+ "import sys;print(sys.version_info[:3])"],
+ capture_output=True)
+ return eval(res.stdout.decode("utf-8"))
+ return None
+
def _setup_clang(self):
if sys.platform != "win32":
return
setup_script_dir = os.path.abspath(os.path.join(this_dir, ".."))
sys.path.append(setup_script_dir)
-from build_scripts.utils import (find_files_using_glob, find_glob_in_path,
+from build_scripts.utils import (find_files_using_glob, find_glob_in_path, # noqa: E402
remove_tree, run_process, run_process_output)
-from build_scripts.log import log
+from build_scripts.log import log # noqa: E402
log.setLevel(logging.DEBUG)
# Note: shiboken6_generator is not needed for compile_using_nuitka,
# but building modules with cmake needs it.
if NEW_WHEELS:
- return ["shiboken6", "shiboken6_generator", "PySide6_Essentials", "PySide6_Addons", "PySide6"]
+ return ["shiboken6", "shiboken6_generator", "PySide6_Essentials", "PySide6_Addons",
+ "PySide6"]
else:
return ["shiboken6", "shiboken6_generator", "PySide6"]
for modname in modules:
# PYSIDE-1735: pyi files are no longer compatible with Python.
# XXX Maybe add a test with Mypy here?
- pass # execute_script(src_path / f"{modname}.pyi")
+ pass # execute_script(src_path / f"{modname}.pyi")
def run_wheel_tests(install_wheels, wheels_dir_name):
)
parser.add_argument("--qmake", type=str, help="Path to qmake")
parser.add_argument("--cmake", type=str, help="Path to cmake")
- parser.add_argument("--wheels-dir", type=str, help="Path to where the wheels are", default="dist")
+ parser.add_argument("--wheels-dir", type=str, help="Path to where the wheels are",
+ default="dist")
parser.add_argument("--new", action="store_true", help="Option to test new wheels")
options = parser.parse_args()
QMAKE_PATH = find_executable("qmake", options.qmake)
print(f"NDK path found in {str(ndk_version_path)}")
else:
ndk_path.mkdir(parents=True, exist_ok=True)
- url = f"https://dl.google.com/android/repository/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"
+ url = (f"https://dl.google.com/android/repository"
+ f"/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip")
print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
_download(url=url, destination=ndk_zip_path)
print("Unpacking Android Ndk")
- _unpack(zip_file=(ndk_path /
- f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
+ _unpack(zip_file=(ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
destination=ndk_path)
return ndk_version_path
import sys
import logging
import argparse
-import tempfile
-import subprocess
import stat
import warnings
+import shutil
from dataclasses import dataclass
-from typing import List
from pathlib import Path
from git import Repo, RemoteProgress
from jinja2 import Environment, FileSystemLoader
from android_utilities import (run_command, download_android_commandlinetools,
- download_android_ndk, install_android_packages)
+ download_android_ndk, install_android_packages)
# Note: Does not work with PyEnv. Your Host Python should contain openssl.
PYTHON_VERSION = "3.10"
-APIC_HELP = ('''
-Points to the installation path of Python for the specific Android
-platform. If the path given does not exist, then Python for Android
-is cross compiled for the specific platform and installed into this
-path as <path>/Python-'plat_name'/_install.
-
-If this path is not given, then Python for Android is cross-compiled
-into a temportary directory, which is deleted when the Qt for Python
-Android wheels are created.
-''')
-
SKIP_UPDATE_HELP = ("skip the updation of SDK packages build-tools, platform-tools to"
" latest version")
accept the license manually through command line.
''')
+CLEAN_CACHE_HELP = ('''
+Cleans cache stored in $HOME/.pyside6_deploy_cache.
+Options:
+
+1. all - all the cache including Android Ndk, Android Sdk and Cross-compiled Python are deleted.
+2. ndk - Only the Android Ndk is deleted.
+3. sdk - Only the Android Sdk is deleted.
+4. python - The cross compiled Python for all platforms, the cloned CPython, the cross compilation
+ scripts for all platforms are deleted.
+5. toolchain - The CMake toolchain file required for cross-compiling Qt for Python, for all
+ platforms are deleted.
+
+If --clean-cache is used and no explicit value is suppied, then `all` is used as default.
+''')
+
@dataclass
class PlatformData:
formatter_class=argparse.RawTextHelpFormatter,
)
- parser.add_argument("-p", "--plat-name", type=str, required=True,
+ parser.add_argument("-p", "--plat-name", type=str, nargs="*",
choices=["aarch64", "armv7a", "i686", "x86_64"],
- help="Android target platform name")
+ default=["aarch64", "armv7a", "i686", "x86_64"], dest="plat_names",
+ help="Android target platforms")
parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
dest="loglevel", const=logging.INFO)
parser.add_argument("--api-level", type=str, default="31", help="Android API level to use")
- parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred 25b)")
+ parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
# sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
parser.add_argument("--qt-install-path", type=str, required=not occp_exists(),
parser.add_argument("-occp", "--only-cross-compile-python", action="store_true",
help="Only cross compiles Python for the specified Android platform")
- parser.add_argument("-apic", "--android-python-install-path", type=str, default=None,
- required=occp_exists(),
- help=APIC_HELP)
-
parser.add_argument("--dry-run", action="store_true", help="show the commands to be run")
parser.add_argument("--skip-update", action="store_true",
parser.add_argument("--auto-accept-license", action="store_true",
help=ACCEPT_LICENSE_HELP)
+ parser.add_argument("--clean-cache", type=str, nargs="?", const="all",
+ choices=["all", "python", "ndk", "sdk", "toolchain"],
+ help=CLEAN_CACHE_HELP)
+
args = parser.parse_args()
logging.basicConfig(level=args.loglevel)
ndk_path = args.ndk_path
sdk_path = args.sdk_path
only_py_cross_compile = args.only_cross_compile_python
- python_path = args.android_python_install_path
- # the same android platforms are named differently in CMake, Cpython and Qt.
- # Hence, we need to distinguish them
- qt_plat_name = None
android_abi = None
gcc_march = None
plat_bits = None
dry_run = args.dry_run
- plat_name = args.plat_name
+ plat_names = args.plat_names
api_level = args.api_level
skip_update = args.skip_update
auto_accept_license = args.auto_accept_license
+ clean_cache = args.clean_cache
# auto download Android NDK and SDK
pyside6_deploy_cache = Path.home() / ".pyside6_android_deploy"
+ logging.info(f"Cache created at {str(pyside6_deploy_cache.resolve())}")
+ pyside6_deploy_cache.mkdir(exist_ok=True)
+
+ if pyside6_deploy_cache.exists() and clean_cache:
+ if clean_cache == "all":
+ shutil.rmtree(pyside6_deploy_cache)
+ elif clean_cache == "ndk":
+ cached_ndk_dir = pyside6_deploy_cache / "android-ndk"
+ if cached_ndk_dir.exists():
+ shutil.rmtree(cached_ndk_dir)
+ elif clean_cache == "sdk":
+ cached_sdk_dir = pyside6_deploy_cache / "android-sdk"
+ if cached_sdk_dir.exists():
+ shutil.rmtree(cached_sdk_dir)
+ elif clean_cache == "python":
+ cached_cpython_dir = pyside6_deploy_cache / "cpython"
+ if cached_cpython_dir.exists():
+ shutil.rmtree(pyside6_deploy_cache / "cpython")
+ for cc_python_path in pyside6_deploy_cache.glob("Python-*"):
+ if cc_python_path.is_dir():
+ shutil.rmtree(cc_python_path)
+ elif clean_cache == "toolchain":
+ for toolchain_path in pyside6_deploy_cache.glob("toolchain_*"):
+ if toolchain_path.is_file():
+ toolchain_path.unlink()
if not ndk_path:
# Download android ndk
install_android_packages(android_sdk_dir=sdk_path, android_api=api_level, dry_run=dry_run,
accept_license=auto_accept_license, skip_update=skip_update)
- # python path is valid, if Python for android installation exists in python_path
- valid_python_path = True
- if python_path and Path(python_path).exists():
- expected_dirs = ["lib", "include"]
- for expected_dir in expected_dirs:
- if not (Path(python_path) / expected_dir).is_dir():
- valid_python_path = False
- warnings.warn(
- "Given target Python, given through --android-python-install-path does not"
- "contain Python. New Python for android will be cross compiled and installed"
- "in this directory"
- )
- break
-
templates_path = Path(__file__).parent / "templates"
- # for armv7a the API level dependent binaries like clang are named
- # armv7a-linux-androideabi27-clang, as opposed to other platforms which
- # are named like x86_64-linux-android27-clang
- platform_data = None
- if plat_name == "armv7a":
- platform_data = PlatformData("armv7a", f"eabi{api_level}", "armeabi-v7a", "armv7", "armv7",
- "32")
- elif plat_name == "aarch64":
- platform_data = PlatformData("aarch64", api_level, "arm64-v8a", "arm64_v8a", "armv8-a", "64")
- elif plat_name == "i686":
- platform_data = PlatformData("i686", api_level, "x86", "x86", "i686", "32")
- else: # plat_name is x86_64
- platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
-
- # clone cpython and checkout 3.10
- with tempfile.TemporaryDirectory() as temp_dir:
+ for plat_name in plat_names:
+ # for armv7a the API level dependent binaries like clang are named
+ # armv7a-linux-androideabi27-clang, as opposed to other platforms which
+ # are named like x86_64-linux-android27-clang
+ platform_data = None
+ if plat_name == "armv7a":
+ platform_data = PlatformData("armv7a", api_level, "armeabi-v7a", "armv7",
+ "armv7", "32")
+ elif plat_name == "aarch64":
+ platform_data = PlatformData("aarch64", api_level, "arm64-v8a", "arm64_v8a", "armv8-a",
+ "64")
+ elif plat_name == "i686":
+ platform_data = PlatformData("i686", api_level, "x86", "x86", "i686", "32")
+ else: # plat_name is x86_64
+ platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
+
+ # python path is valid, if Python for android installation exists in python_path
+ python_path = (pyside6_deploy_cache / f"Python-{platform_data.plat_name}-linux-android"
+ / "_install")
+ valid_python_path = python_path.exists()
+ if Path(python_path).exists():
+ expected_dirs = ["lib", "include"]
+ for expected_dir in expected_dirs:
+ if not (Path(python_path) / expected_dir).is_dir():
+ valid_python_path = False
+ warnings.warn(
+ f"{str(python_path.resolve())} is corrupted. New Python for {plat_name} "
+ f"android will be cross-compiled into {str(pyside6_deploy_cache.resolve())}"
+ )
+ break
+
environment = Environment(loader=FileSystemLoader(templates_path))
- temp_dir = Path(temp_dir)
- logging.info(f"temp dir created at {temp_dir}")
- if not python_path or not valid_python_path:
- cpython_dir = temp_dir / "cpython"
- python_ccompile_script = cpython_dir / "cross_compile.sh"
-
- logging.info(f"cloning cpython {PYTHON_VERSION}")
- Repo.clone_from(
- "https://github.com/python/cpython.git",
- cpython_dir,
- progress=CloneProgress(),
- branch=PYTHON_VERSION,
- )
+ if not valid_python_path:
+ # clone cpython and checkout 3.10
+ cpython_dir = pyside6_deploy_cache / "cpython"
+ python_ccompile_script = cpython_dir / f"cross_compile_{plat_name}.sh"
+
+ if not cpython_dir.exists():
+ logging.info(f"cloning cpython {PYTHON_VERSION}")
+ Repo.clone_from(
+ "https://github.com/python/cpython.git",
+ cpython_dir,
+ progress=CloneProgress(),
+ branch=PYTHON_VERSION,
+ )
- if not python_path:
- android_py_install_path_prefix = temp_dir
- else:
- android_py_install_path_prefix = python_path
+ if not python_ccompile_script.exists():
+ # use jinja2 to create cross_compile.sh script
+ template = environment.get_template("cross_compile.tmpl.sh")
+ content = template.render(
+ plat_name=platform_data.plat_name,
+ ndk_path=ndk_path,
+ api_level=platform_data.api_level,
+ android_py_install_path_prefix=pyside6_deploy_cache,
+ )
- # use jinja2 to create cross_compile.sh script
- template = environment.get_template("cross_compile.tmpl.sh")
- content = template.render(
- plat_name=platform_data.plat_name,
- ndk_path=ndk_path,
- api_level=platform_data.api_level,
- android_py_install_path_prefix=android_py_install_path_prefix,
- )
+ logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
+ with open(python_ccompile_script, mode="w", encoding="utf-8") as ccompile_script:
+ ccompile_script.write(content)
- logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
- with open(python_ccompile_script, mode="w", encoding="utf-8") as ccompile_script:
- ccompile_script.write(content)
+ # give run permission to cross compile script
+ python_ccompile_script.chmod(python_ccompile_script.stat().st_mode | stat.S_IEXEC)
- # give run permission to cross compile script
- python_ccompile_script.chmod(python_ccompile_script.stat().st_mode | stat.S_IEXEC)
+ # clean built files
+ logging.info("Cleaning CPython built files")
+ run_command(["make", "distclean"], cwd=cpython_dir, dry_run=dry_run, ignore_fail=True)
# run the cross compile script
logging.info(f"Running Python cross-compile for platform {platform_data.plat_name}")
- run_command(["./cross_compile.sh"], cwd=cpython_dir, dry_run=dry_run, show_stdout=True)
-
- python_path = (f"{android_py_install_path_prefix}/Python-{platform_data.plat_name}-linux-android/"
- "_install")
+ run_command([f"./{python_ccompile_script.name}"], cwd=cpython_dir, dry_run=dry_run,
+ show_stdout=True)
# run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
# libpython3.x.so, to match with python_for_android's Python library. Otherwise,
logging.info(
f"Cross compile Python for Android platform {platform_data.plat_name}. "
- f"Final installation in "
- f"{python_path}"
+ f"Final installation in {python_path}"
)
if only_py_cross_compile:
- sys.exit(0)
-
- qfp_toolchain = temp_dir / f"toolchain_{platform_data.plat_name}.cmake"
- template = environment.get_template("toolchain_default.tmpl.cmake")
- content = template.render(
- ndk_path=ndk_path,
- sdk_path=sdk_path,
- api_level=platform_data.api_level,
- qt_install_path=qt_install_path,
- plat_name=platform_data.plat_name,
- android_abi=platform_data.android_abi,
- qt_plat_name=platform_data.qt_plat_name,
- gcc_march=platform_data.gcc_march,
- plat_bits=platform_data.plat_bits,
- python_version=PYTHON_VERSION,
- target_python_path=python_path
- )
-
- logging.info(f"Writing Qt for Python toolchain file into"
- f"{qfp_toolchain}")
- with open(qfp_toolchain, mode="w", encoding="utf-8") as ccompile_script:
- ccompile_script.write(content)
-
- # give run permission to cross compile script
- qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
+ continue
+
+ if only_py_cross_compile:
+ requested_platforms = ",".join(plat_names)
+ print(f"Python for Android platforms: {requested_platforms} cross compiled "
+ f"to {str(pyside6_deploy_cache)}")
+ sys.exit(0)
+
+ qfp_toolchain = pyside6_deploy_cache / f"toolchain_{platform_data.plat_name}.cmake"
+
+ if not qfp_toolchain.exists():
+ template = environment.get_template("toolchain_default.tmpl.cmake")
+ content = template.render(
+ ndk_path=ndk_path,
+ sdk_path=sdk_path,
+ api_level=platform_data.api_level,
+ qt_install_path=qt_install_path,
+ plat_name=platform_data.plat_name,
+ android_abi=platform_data.android_abi,
+ qt_plat_name=platform_data.qt_plat_name,
+ gcc_march=platform_data.gcc_march,
+ plat_bits=platform_data.plat_bits,
+ python_version=PYTHON_VERSION,
+ target_python_path=python_path
+ )
+
+ logging.info(f"Writing Qt for Python toolchain file into {qfp_toolchain}")
+ with open(qfp_toolchain, mode="w", encoding="utf-8") as ccompile_script:
+ ccompile_script.write(content)
+
+ # give run permission to cross compile script
+ qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
# run the cross compile script
logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
- "--ignore-git", "--standalone", "--limited-api=yes",
+ "--standalone", "--limited-api=yes",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
f"--qt-host-path={qt_install_path}/gcc_64",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
(f"--qt-target-path={qt_install_path}/"
- f"android_{platform_data.qt_plat_name}"),
- "--no-qt-tools", "--skip-docs", "--unity"]
+ f"android_{platform_data.qt_plat_name}"),
+ "--no-qt-tools", "--unity"]
run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/linux-x86_64/bin
export TOOL_PREFIX=$TOOLCHAIN/$HOST_ARCH
export PLATFORM_API={{ api_level }}
+{% if plat_name == "armv7a" -%}
+export CXX=${TOOL_PREFIX}eabi${PLATFORM_API}-clang++
+export CPP="${TOOL_PREFIX}eabi${PLATFORM_API}-clang++ -E"
+export CC=${TOOL_PREFIX}eabi${PLATFORM_API}-clang
+{% else %}
export CXX=${TOOL_PREFIX}${PLATFORM_API}-clang++
export CPP="${TOOL_PREFIX}${PLATFORM_API}-clang++ -E"
+export CC=${TOOL_PREFIX}${PLATFORM_API}-clang
+{% endif %}
export AR=$TOOLCHAIN/llvm-ar
export RANLIB=$TOOLCHAIN/llvm-ranlib
-export CC=$TOOL_PREFIX${PLATFORM_API}-clang
export LD=$TOOLCHAIN/ld
export READELF=$TOOLCHAIN/llvm-readelf
export CFLAGS='-fPIC -DANDROID'
cmake_minimum_required(VERSION 3.18)
include_guard(GLOBAL)
set(CMAKE_SYSTEM_NAME Android)
+{% if plat_name == "armv7a" -%}
+set(CMAKE_SYSTEM_PROCESSOR armv7-a)
+{% else %}
set(CMAKE_SYSTEM_PROCESSOR {{ plat_name }})
+{% endif %}
set(CMAKE_ANDROID_API {{ api_level }})
set(CMAKE_ANDROID_NDK {{ ndk_path }})
set(CMAKE_ANDROID_ARCH_ABI {{ android_abi }})
set(ANDROID_PLATFORM "android-{{ api_level }}" CACHE STRING "")
endif()
set(ANDROID_SDK_ROOT {{ sdk_path }})
-
-set(QT_COMPILER_FLAGS "--target={{ plat_name }}-linux-android{{ api_level }} \
+{% if plat_name == "armv7a" -%}
+set(_TARGET_NAME_ENDING "eabi{{ api_level }}")
+{% else %}
+set(_TARGET_NAME_ENDING "{{ api_level }}")
+{% endif %}
+set(QT_COMPILER_FLAGS "--target={{ plat_name }}-linux-android${_TARGET_NAME_ENDING} \
-fomit-frame-pointer \
-march={{ gcc_march }} \
-msse4.2 \
parser.setContentHandler(handler)
parser.parse(typesystem_file)
except Exception as e:
- print(f"Error parsing {typesystem_file}: {e}", file=sys.stderr)
+ print(f"Warning: XML error parsing {typesystem_file}: {e}", file=sys.stderr)
return handler.required_modules
requires-python = ">=3.8, <3.13"
keywords = ["Qt"]
license = {text = "LGPL"}
+dependencies = PROJECT_DEPENDENCIES
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
Changelog = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/doc/changelogs"
Tracker = "https://bugreports.qt.io/projects/PYSIDE"
+PROJECT_SCRIPTS
+
[tool.distutils.bdist_wheel]
py_limited_api = "cp38"
plat_name = PROJECT_TAG
-[tool.setuptools.packages]
-find = {}
-
[tool.setuptools.dynamic]
version = {attr = PROJECT_VERSION}
setup_args = dict(
include_package_data=True,
packages = ["{name}"],
- entry_points = {console_scripts},
ext_modules = [Extension("{fake_ext}", [], py_limited_api=True)],
- install_requires={install},
cmdclass=dict([("build_ext", build_ext)]),
)
setup(**setup_args)